summaryrefslogtreecommitdiff
path: root/ext/pdo_sqlite/sqlite/src/btree.c
diff options
context:
space:
mode:
authorMark A. Hershberger <mah@debian.(none)>2009-03-25 00:37:27 -0400
committerMark A. Hershberger <mah@debian.(none)>2009-03-25 00:37:27 -0400
commit2d4e5b09576bb4f0ba716cc82cdf29ea04d9184b (patch)
tree41ccc042009cba53e4ce43e727fcba4c1cfbf7f3 /ext/pdo_sqlite/sqlite/src/btree.c
parentd29a4fd2dd3b5d4cf6e80b602544d7b71d794e76 (diff)
downloadphp-upstream/5.2.2.tar.gz
Imported Upstream version 5.2.2upstream/5.2.2
Diffstat (limited to 'ext/pdo_sqlite/sqlite/src/btree.c')
-rw-r--r--ext/pdo_sqlite/sqlite/src/btree.c1124
1 files changed, 573 insertions, 551 deletions
diff --git a/ext/pdo_sqlite/sqlite/src/btree.c b/ext/pdo_sqlite/sqlite/src/btree.c
index 52bc749a3..3df2a3b01 100644
--- a/ext/pdo_sqlite/sqlite/src/btree.c
+++ b/ext/pdo_sqlite/sqlite/src/btree.c
@@ -22,13 +22,13 @@
** entries and N+1 pointers to subpages.
**
** ----------------------------------------------------------------
-** | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N) | Ptr(N+1) |
+** | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N-1) | Ptr(N) |
** ----------------------------------------------------------------
**
** All of the keys on the page that Ptr(0) points to have values less
** than Key(0). All of the keys on page Ptr(1) and its subpages have
** values greater than Key(0) and less than Key(1). All of the keys
-** on Ptr(N+1) and its subpages have values greater than Key(N). And
+** on Ptr(N) and its subpages have values greater than Key(N-1). And
** so forth.
**
** Finding a particular key requires reading O(log(M)) pages from the
@@ -41,7 +41,7 @@
** page. If the payload is larger than the preset amount then surplus
** bytes are stored on overflow pages. The payload for an entry
** and the preceding pointer are combined to form a "Cell". Each
-** page has a small header which contains the Ptr(N+1) pointer and other
+** page has a small header which contains the Ptr(N) pointer and other
** information such as the size of key and data.
**
** FORMAT DETAILS
@@ -123,7 +123,7 @@
** 3 2 number of cells on this page
** 5 2 first byte of the cell content area
** 7 1 number of fragmented free bytes
-** 8 4 Right child (the Ptr(N+1) value). Omitted on leaves.
+** 8 4 Right child (the Ptr(N) value). Omitted on leaves.
**
** The flags define the format of this btree page. The leaf flag means that
** this page has no children. The zerodata flag means that this page carries
@@ -291,6 +291,7 @@ struct MemPage {
} aOvfl[5];
BtShared *pBt; /* Pointer back to BTree structure */
u8 *aData; /* Pointer back to the start of the page */
+ DbPage *pDbPage; /* Pager page handle */
Pgno pgno; /* Page number for this page */
MemPage *pParent; /* The parent of this page. NULL for root */
};
@@ -365,6 +366,7 @@ struct CellInfo {
u8 *pCell; /* Pointer to the start of cell content */
i64 nKey; /* The key for INTKEY tables, or number of bytes in key */
u32 nData; /* Number of bytes of data */
+ u32 nPayload; /* Total amount of payload */
u16 nHeader; /* Size of the cell content header in bytes */
u16 nLocal; /* Amount of payload held locally */
u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */
@@ -387,17 +389,13 @@ struct BtCursor {
CellInfo info; /* A parse of the cell we are pointing at */
u8 wrFlag; /* True if writable */
u8 eState; /* One of the CURSOR_XXX constants (see below) */
-#ifndef SQLITE_OMIT_SHARED_CACHE
void *pKey; /* Saved key that was cursor's last known position */
i64 nKey; /* Size of pKey, or last integer key */
int skip; /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */
-#endif
};
/*
-** Potential values for BtCursor.eState. The first two values (VALID and
-** INVALID) may occur in any build. The third (REQUIRESEEK) may only occur
-** if sqlite was compiled without the OMIT_SHARED_CACHE symbol defined.
+** Potential values for BtCursor.eState.
**
** CURSOR_VALID:
** Cursor points to a valid entry. getPayload() etc. may be called.
@@ -425,7 +423,8 @@ struct BtCursor {
*/
#if SQLITE_TEST
# define TRACE(X) if( sqlite3_btree_trace )\
- { sqlite3DebugPrintf X; fflush(stdout); }
+/* { sqlite3DebugPrintf X; fflush(stdout); } */ \
+{ printf X; fflush(stdout); }
int sqlite3_btree_trace=0; /* True to enable tracing */
#else
# define TRACE(X)
@@ -434,7 +433,7 @@ int sqlite3_btree_trace=0; /* True to enable tracing */
/*
** Forward declaration
*/
-static int checkReadLocks(BtShared*,Pgno,BtCursor*);
+static int checkReadLocks(Btree*,Pgno,BtCursor*);
/*
** Read or write a two- and four-byte big-endian integer values.
@@ -509,105 +508,8 @@ struct BtLock {
#define queryTableLock(a,b,c) SQLITE_OK
#define lockTable(a,b,c) SQLITE_OK
#define unlockAllTables(a)
- #define restoreOrClearCursorPosition(a,b) SQLITE_OK
- #define saveAllCursors(a,b,c) SQLITE_OK
-
#else
-static void releasePage(MemPage *pPage);
-
-/*
-** Save the current cursor position in the variables BtCursor.nKey
-** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
-*/
-static int saveCursorPosition(BtCursor *pCur){
- int rc;
-
- assert( CURSOR_VALID==pCur->eState );
- assert( 0==pCur->pKey );
-
- rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
-
- /* If this is an intKey table, then the above call to BtreeKeySize()
- ** stores the integer key in pCur->nKey. In this case this value is
- ** all that is required. Otherwise, if pCur is not open on an intKey
- ** table, then malloc space for and store the pCur->nKey bytes of key
- ** data.
- */
- if( rc==SQLITE_OK && 0==pCur->pPage->intKey){
- void *pKey = sqliteMalloc(pCur->nKey);
- if( pKey ){
- rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey);
- if( rc==SQLITE_OK ){
- pCur->pKey = pKey;
- }else{
- sqliteFree(pKey);
- }
- }else{
- rc = SQLITE_NOMEM;
- }
- }
- assert( !pCur->pPage->intKey || !pCur->pKey );
-
- if( rc==SQLITE_OK ){
- releasePage(pCur->pPage);
- pCur->pPage = 0;
- pCur->eState = CURSOR_REQUIRESEEK;
- }
-
- return rc;
-}
-
-/*
-** Save the positions of all cursors except pExcept open on the table
-** with root-page iRoot. Usually, this is called just before cursor
-** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
-*/
-static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
- BtCursor *p;
- if( sqlite3ThreadDataReadOnly()->useSharedData ){
- for(p=pBt->pCursor; p; p=p->pNext){
- if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) &&
- p->eState==CURSOR_VALID ){
- int rc = saveCursorPosition(p);
- if( SQLITE_OK!=rc ){
- return rc;
- }
- }
- }
- }
- return SQLITE_OK;
-}
-
-/*
-** Restore the cursor to the position it was in (or as close to as possible)
-** when saveCursorPosition() was called. Note that this call deletes the
-** saved position info stored by saveCursorPosition(), so there can be
-** at most one effective restoreOrClearCursorPosition() call after each
-** saveCursorPosition().
-**
-** If the second argument argument - doSeek - is false, then instead of
-** returning the cursor to it's saved position, any saved position is deleted
-** and the cursor state set to CURSOR_INVALID.
-*/
-static int restoreOrClearCursorPositionX(BtCursor *pCur, int doSeek){
- int rc = SQLITE_OK;
- assert( sqlite3ThreadDataReadOnly()->useSharedData );
- assert( pCur->eState==CURSOR_REQUIRESEEK );
- pCur->eState = CURSOR_INVALID;
- if( doSeek ){
- rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip);
- }
- if( rc==SQLITE_OK ){
- sqliteFree(pCur->pKey);
- pCur->pKey = 0;
- assert( CURSOR_VALID==pCur->eState || CURSOR_INVALID==pCur->eState );
- }
- return rc;
-}
-
-#define restoreOrClearCursorPosition(p,x) \
- (p->eState==CURSOR_REQUIRESEEK?restoreOrClearCursorPositionX(p,x):SQLITE_OK)
/*
** Query to see if btree handle p may obtain a lock of type eLock
@@ -747,6 +649,105 @@ static void unlockAllTables(Btree *p){
}
#endif /* SQLITE_OMIT_SHARED_CACHE */
+static void releasePage(MemPage *pPage); /* Forward reference */
+
+/*
+** Save the current cursor position in the variables BtCursor.nKey
+** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
+*/
+static int saveCursorPosition(BtCursor *pCur){
+ int rc;
+
+ assert( CURSOR_VALID==pCur->eState );
+ assert( 0==pCur->pKey );
+
+ rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
+
+ /* If this is an intKey table, then the above call to BtreeKeySize()
+ ** stores the integer key in pCur->nKey. In this case this value is
+ ** all that is required. Otherwise, if pCur is not open on an intKey
+ ** table, then malloc space for and store the pCur->nKey bytes of key
+ ** data.
+ */
+ if( rc==SQLITE_OK && 0==pCur->pPage->intKey){
+ void *pKey = sqliteMalloc(pCur->nKey);
+ if( pKey ){
+ rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey);
+ if( rc==SQLITE_OK ){
+ pCur->pKey = pKey;
+ }else{
+ sqliteFree(pKey);
+ }
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+ assert( !pCur->pPage->intKey || !pCur->pKey );
+
+ if( rc==SQLITE_OK ){
+ releasePage(pCur->pPage);
+ pCur->pPage = 0;
+ pCur->eState = CURSOR_REQUIRESEEK;
+ }
+
+ return rc;
+}
+
+/*
+** Save the positions of all cursors except pExcept open on the table
+** with root-page iRoot. Usually, this is called just before cursor
+** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
+*/
+static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
+ BtCursor *p;
+ for(p=pBt->pCursor; p; p=p->pNext){
+ if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) &&
+ p->eState==CURSOR_VALID ){
+ int rc = saveCursorPosition(p);
+ if( SQLITE_OK!=rc ){
+ return rc;
+ }
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Clear the current cursor position.
+*/
+static void clearCursorPosition(BtCursor *pCur){
+ sqliteFree(pCur->pKey);
+ pCur->pKey = 0;
+ pCur->eState = CURSOR_INVALID;
+}
+
+/*
+** Restore the cursor to the position it was in (or as close to as possible)
+** when saveCursorPosition() was called. Note that this call deletes the
+** saved position info stored by saveCursorPosition(), so there can be
+** at most one effective restoreOrClearCursorPosition() call after each
+** saveCursorPosition().
+**
+** If the second argument argument - doSeek - is false, then instead of
+** returning the cursor to it's saved position, any saved position is deleted
+** and the cursor state set to CURSOR_INVALID.
+*/
+static int restoreOrClearCursorPositionX(BtCursor *pCur){
+ int rc;
+ assert( pCur->eState==CURSOR_REQUIRESEEK );
+ pCur->eState = CURSOR_INVALID;
+ rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skip);
+ if( rc==SQLITE_OK ){
+ sqliteFree(pCur->pKey);
+ pCur->pKey = 0;
+ assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID );
+ }
+ return rc;
+}
+
+#define restoreOrClearCursorPosition(p) \
+ (p->eState==CURSOR_REQUIRESEEK?restoreOrClearCursorPositionX(p):SQLITE_OK)
+
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** These macros define the location of the pointer-map entry for a
@@ -822,9 +823,10 @@ static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){
** An error code is returned if something goes wrong, otherwise SQLITE_OK.
*/
static int ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent){
- u8 *pPtrmap; /* The pointer map page */
- Pgno iPtrmap; /* The pointer map page number */
- int offset; /* Offset in pointer map page */
+ DbPage *pDbPage; /* The pointer map page */
+ u8 *pPtrmap; /* The pointer map data */
+ Pgno iPtrmap; /* The pointer map page number */
+ int offset; /* Offset in pointer map page */
int rc;
/* The master-journal page number must never be used as a pointer map page */
@@ -835,22 +837,23 @@ static int ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent){
return SQLITE_CORRUPT_BKPT;
}
iPtrmap = PTRMAP_PAGENO(pBt, key);
- rc = sqlite3pager_get(pBt->pPager, iPtrmap, (void **)&pPtrmap);
+ rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage);
if( rc!=SQLITE_OK ){
return rc;
}
offset = PTRMAP_PTROFFSET(pBt, key);
+ pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage);
if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){
TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent));
- rc = sqlite3pager_write(pPtrmap);
+ rc = sqlite3PagerWrite(pDbPage);
if( rc==SQLITE_OK ){
pPtrmap[offset] = eType;
put4byte(&pPtrmap[offset+1], parent);
}
}
- sqlite3pager_unref(pPtrmap);
+ sqlite3PagerUnref(pDbPage);
return rc;
}
@@ -862,23 +865,25 @@ static int ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent){
** An error code is returned if something goes wrong, otherwise SQLITE_OK.
*/
static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
+ DbPage *pDbPage; /* The pointer map page */
int iPtrmap; /* Pointer map page index */
u8 *pPtrmap; /* Pointer map page data */
int offset; /* Offset of entry in pointer map */
int rc;
iPtrmap = PTRMAP_PAGENO(pBt, key);
- rc = sqlite3pager_get(pBt->pPager, iPtrmap, (void **)&pPtrmap);
+ rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage);
if( rc!=0 ){
return rc;
}
+ pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage);
offset = PTRMAP_PTROFFSET(pBt, key);
assert( pEType!=0 );
*pEType = pPtrmap[offset];
if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]);
- sqlite3pager_unref(pPtrmap);
+ sqlite3PagerUnref(pDbPage);
if( *pEType<1 || *pEType>5 ) return SQLITE_CORRUPT_BKPT;
return SQLITE_OK;
}
@@ -952,6 +957,7 @@ static void parseCellPtr(
pInfo->nKey = x;
nPayload += x;
}
+ pInfo->nPayload = nPayload;
pInfo->nHeader = n;
if( nPayload<=pPage->maxLocal ){
/* This is the (easy) common case where the entire payload fits
@@ -1028,6 +1034,7 @@ static int ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell){
if( pCell ){
CellInfo info;
parseCellPtr(pPage, pCell, &info);
+ assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload );
if( (info.nData+(pPage->intKey?0:info.nKey))>info.nLocal ){
Pgno ovfl = get4byte(&pCell[info.iOverflow]);
return ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno);
@@ -1048,91 +1055,6 @@ static int ptrmapPutOvfl(MemPage *pPage, int iCell){
#endif
-/*
-** Do sanity checking on a page. Throw an exception if anything is
-** not right.
-**
-** This routine is used for internal error checking only. It is omitted
-** from most builds.
-*/
-#if defined(BTREE_DEBUG) && !defined(NDEBUG) && 0
-static void _pageIntegrity(MemPage *pPage){
- int usableSize;
- u8 *data;
- int i, j, idx, c, pc, hdr, nFree;
- int cellOffset;
- int nCell, cellLimit;
- u8 *used;
-
- used = sqliteMallocRaw( pPage->pBt->pageSize );
- if( used==0 ) return;
- usableSize = pPage->pBt->usableSize;
- assert( pPage->aData==&((unsigned char*)pPage)[-pPage->pBt->pageSize] );
- hdr = pPage->hdrOffset;
- assert( hdr==(pPage->pgno==1 ? 100 : 0) );
- assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) );
- c = pPage->aData[hdr];
- if( pPage->isInit ){
- assert( pPage->leaf == ((c & PTF_LEAF)!=0) );
- assert( pPage->zeroData == ((c & PTF_ZERODATA)!=0) );
- assert( pPage->leafData == ((c & PTF_LEAFDATA)!=0) );
- assert( pPage->intKey == ((c & (PTF_INTKEY|PTF_LEAFDATA))!=0) );
- assert( pPage->hasData ==
- !(pPage->zeroData || (!pPage->leaf && pPage->leafData)) );
- assert( pPage->cellOffset==pPage->hdrOffset+12-4*pPage->leaf );
- assert( pPage->nCell = get2byte(&pPage->aData[hdr+3]) );
- }
- data = pPage->aData;
- memset(used, 0, usableSize);
- for(i=0; i<hdr+10-pPage->leaf*4; i++) used[i] = 1;
- nFree = 0;
- pc = get2byte(&data[hdr+1]);
- while( pc ){
- int size;
- assert( pc>0 && pc<usableSize-4 );
- size = get2byte(&data[pc+2]);
- assert( pc+size<=usableSize );
- nFree += size;
- for(i=pc; i<pc+size; i++){
- assert( used[i]==0 );
- used[i] = 1;
- }
- pc = get2byte(&data[pc]);
- }
- idx = 0;
- nCell = get2byte(&data[hdr+3]);
- cellLimit = get2byte(&data[hdr+5]);
- assert( pPage->isInit==0
- || pPage->nFree==nFree+data[hdr+7]+cellLimit-(cellOffset+2*nCell) );
- cellOffset = pPage->cellOffset;
- for(i=0; i<nCell; i++){
- int size;
- pc = get2byte(&data[cellOffset+2*i]);
- assert( pc>0 && pc<usableSize-4 );
- size = cellSize(pPage, &data[pc]);
- assert( pc+size<=usableSize );
- for(j=pc; j<pc+size; j++){
- assert( used[j]==0 );
- used[j] = 1;
- }
- }
- for(i=cellOffset+2*nCell; i<cellimit; i++){
- assert( used[i]==0 );
- used[i] = 1;
- }
- nFree = 0;
- for(i=0; i<usableSize; i++){
- assert( used[i]<=1 );
- if( used[i]==0 ) nFree++;
- }
- assert( nFree==data[hdr+7] );
- sqliteFree(used);
-}
-#define pageIntegrity(X) _pageIntegrity(X)
-#else
-# define pageIntegrity(X)
-#endif
-
/* A bunch of assert() statements to check the transaction state variables
** of handle p (type Btree*) are internally consistent.
*/
@@ -1161,7 +1083,7 @@ static int defragmentPage(MemPage *pPage){
unsigned char *data; /* The page data */
unsigned char *temp; /* Temp area for cell content */
- assert( sqlite3pager_iswriteable(pPage->aData) );
+ assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( pPage->pBt!=0 );
assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE );
assert( pPage->nOverflow==0 );
@@ -1219,7 +1141,7 @@ static int allocateSpace(MemPage *pPage, int nByte){
unsigned char *data;
data = pPage->aData;
- assert( sqlite3pager_iswriteable(data) );
+ assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( pPage->pBt );
if( nByte<4 ) nByte = 4;
if( pPage->nFree<nByte || pPage->nOverflow>0 ) return 0;
@@ -1276,7 +1198,7 @@ static void freeSpace(MemPage *pPage, int start, int size){
unsigned char *data = pPage->aData;
assert( pPage->pBt!=0 );
- assert( sqlite3pager_iswriteable(data) );
+ assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( start>=pPage->hdrOffset+6+(pPage->leaf?0:4) );
assert( (start + size)<=pPage->pBt->usableSize );
if( size<4 ) size = 4;
@@ -1385,7 +1307,7 @@ static int initPage(
pBt = pPage->pBt;
assert( pBt!=0 );
assert( pParent==0 || pParent->pBt==pBt );
- assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) );
+ assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
assert( pPage->aData == &((unsigned char*)pPage)[-pBt->pageSize] );
if( pPage->pParent!=pParent && (pPage->pParent!=0 || pPage->isInit) ){
/* The parent page should never change unless the file is corrupt */
@@ -1394,7 +1316,7 @@ static int initPage(
if( pPage->isInit ) return SQLITE_OK;
if( pPage->pParent==0 && pParent!=0 ){
pPage->pParent = pParent;
- sqlite3pager_ref(pParent->aData);
+ sqlite3PagerRef(pParent->pDbPage);
}
hdr = pPage->hdrOffset;
data = pPage->aData;
@@ -1439,7 +1361,6 @@ static int initPage(
}
pPage->isInit = 1;
- pageIntegrity(pPage);
return SQLITE_OK;
}
@@ -1453,9 +1374,9 @@ static void zeroPage(MemPage *pPage, int flags){
int hdr = pPage->hdrOffset;
int first;
- assert( sqlite3pager_pagenumber(data)==pPage->pgno );
+ assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno );
assert( &data[pBt->pageSize] == (unsigned char*)pPage );
- assert( sqlite3pager_iswriteable(data) );
+ assert( sqlite3PagerIswriteable(pPage->pDbPage) );
memset(&data[hdr], 0, pBt->usableSize - hdr);
data[hdr] = flags;
first = hdr + 8 + 4*((flags&PTF_LEAF)==0);
@@ -1470,21 +1391,29 @@ static void zeroPage(MemPage *pPage, int flags){
pPage->idxShift = 0;
pPage->nCell = 0;
pPage->isInit = 1;
- pageIntegrity(pPage);
}
/*
** Get a page from the pager. Initialize the MemPage.pBt and
** MemPage.aData elements if needed.
+**
+** If the noContent flag is set, it means that we do not care about
+** the content of the page at this time. So do not go to the disk
+** to fetch the content. Just fill in the content with zeros for now.
+** If in the future we call sqlite3PagerWrite() on this page, that
+** means we have started to be concerned about content and the disk
+** read should occur at that point.
*/
-static int getPage(BtShared *pBt, Pgno pgno, MemPage **ppPage){
+static int getPage(BtShared *pBt, Pgno pgno, MemPage **ppPage, int noContent){
int rc;
- unsigned char *aData;
MemPage *pPage;
- rc = sqlite3pager_get(pBt->pPager, pgno, (void**)&aData);
+ DbPage *pDbPage;
+
+ rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, noContent);
if( rc ) return rc;
- pPage = (MemPage*)&aData[pBt->pageSize];
- pPage->aData = aData;
+ pPage = (MemPage *)sqlite3PagerGetExtra(pDbPage);
+ pPage->aData = sqlite3PagerGetData(pDbPage);
+ pPage->pDbPage = pDbPage;
pPage->pBt = pBt;
pPage->pgno = pgno;
pPage->hdrOffset = pPage->pgno==1 ? 100 : 0;
@@ -1507,7 +1436,7 @@ static int getAndInitPage(
if( pgno==0 ){
return SQLITE_CORRUPT_BKPT;
}
- rc = getPage(pBt, pgno, ppPage);
+ rc = getPage(pBt, pgno, ppPage, 0);
if( rc==SQLITE_OK && (*ppPage)->isInit==0 ){
rc = initPage(*ppPage, pParent);
}
@@ -1523,7 +1452,7 @@ static void releasePage(MemPage *pPage){
assert( pPage->aData );
assert( pPage->pBt );
assert( &pPage->aData[pPage->pBt->pageSize]==(unsigned char*)pPage );
- sqlite3pager_unref(pPage->aData);
+ sqlite3PagerUnref(pPage->pDbPage);
}
}
@@ -1532,10 +1461,10 @@ static void releasePage(MemPage *pPage){
** reaches zero. We need to unref the pParent pointer when that
** happens.
*/
-static void pageDestructor(void *pData, int pageSize){
+static void pageDestructor(DbPage *pData, int pageSize){
MemPage *pPage;
assert( (pageSize & 7)==0 );
- pPage = (MemPage*)&((char*)pData)[pageSize];
+ pPage = (MemPage *)sqlite3PagerGetExtra(pData);
if( pPage->pParent ){
MemPage *pParent = pPage->pParent;
pPage->pParent = 0;
@@ -1552,10 +1481,10 @@ static void pageDestructor(void *pData, int pageSize){
** This routine needs to reset the extra data section at the end of the
** page to agree with the restored data.
*/
-static void pageReinit(void *pData, int pageSize){
+static void pageReinit(DbPage *pData, int pageSize){
MemPage *pPage;
assert( (pageSize & 7)==0 );
- pPage = (MemPage*)&((char*)pData)[pageSize];
+ pPage = (MemPage *)sqlite3PagerGetExtra(pData);
if( pPage->isInit ){
pPage->isInit = 0;
initPage(pPage, pPage->pParent);
@@ -1591,9 +1520,9 @@ int sqlite3BtreeOpen(
*/
#if !defined(SQLITE_OMIT_SHARED_CACHE) || !defined(SQLITE_OMIT_AUTOVACUUM)
#ifdef SQLITE_OMIT_MEMORYDB
- const int isMemdb = !zFilename;
+ const int isMemdb = 0;
#else
- const int isMemdb = !zFilename || (strcmp(zFilename, ":memory:")?0:1);
+ const int isMemdb = zFilename && !strcmp(zFilename, ":memory:");
#endif
#endif
@@ -1615,7 +1544,7 @@ int sqlite3BtreeOpen(
}
for(pBt=pTsdro->pBtree; pBt; pBt=pBt->pNext){
assert( pBt->nRef>0 );
- if( 0==strcmp(zFullPathname, sqlite3pager_filename(pBt->pPager)) ){
+ if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager)) ){
p->pBt = pBt;
*ppBtree = p;
pBt->nRef++;
@@ -1644,9 +1573,14 @@ int sqlite3BtreeOpen(
sqliteFree(p);
return SQLITE_NOMEM;
}
- rc = sqlite3pager_open(&pBt->pPager, zFilename, EXTRA_SIZE, flags);
+ rc = sqlite3PagerOpen(&pBt->pPager, zFilename, EXTRA_SIZE, flags);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader);
+ }
if( rc!=SQLITE_OK ){
- if( pBt->pPager ) sqlite3pager_close(pBt->pPager);
+ if( pBt->pPager ){
+ sqlite3PagerClose(pBt->pPager);
+ }
sqliteFree(pBt);
sqliteFree(p);
*ppBtree = 0;
@@ -1654,12 +1588,11 @@ int sqlite3BtreeOpen(
}
p->pBt = pBt;
- sqlite3pager_set_destructor(pBt->pPager, pageDestructor);
- sqlite3pager_set_reiniter(pBt->pPager, pageReinit);
+ sqlite3PagerSetDestructor(pBt->pPager, pageDestructor);
+ sqlite3PagerSetReiniter(pBt->pPager, pageReinit);
pBt->pCursor = 0;
pBt->pPage1 = 0;
- pBt->readOnly = sqlite3pager_isreadonly(pBt->pPager);
- sqlite3pager_read_fileheader(pBt->pPager, sizeof(zDbHeader), zDbHeader);
+ pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager);
pBt->pageSize = get2byte(&zDbHeader[16]);
if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
|| ((pBt->pageSize-1)&pBt->pageSize)!=0 ){
@@ -1691,7 +1624,7 @@ int sqlite3BtreeOpen(
}
pBt->usableSize = pBt->pageSize - nReserve;
assert( (pBt->pageSize & 7)==0 ); /* 8-byte alignment of pageSize */
- sqlite3pager_set_pagesize(pBt->pPager, pBt->pageSize);
+ sqlite3PagerSetPagesize(pBt->pPager, pBt->pageSize);
#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO)
/* Add the new btree to the linked list starting at ThreadData.pBtree.
@@ -1768,7 +1701,7 @@ int sqlite3BtreeClose(Btree *p){
/* Close the pager and free the shared-btree structure */
assert( !pBt->pCursor );
- sqlite3pager_close(pBt->pPager);
+ sqlite3PagerClose(pBt->pPager);
if( pBt->xFreeSchema && pBt->pSchema ){
pBt->xFreeSchema(pBt->pSchema);
}
@@ -1783,7 +1716,7 @@ int sqlite3BtreeClose(Btree *p){
int sqlite3BtreeSetBusyHandler(Btree *p, BusyHandler *pHandler){
BtShared *pBt = p->pBt;
pBt->pBusyHandler = pHandler;
- sqlite3pager_set_busyhandler(pBt->pPager, pHandler);
+ sqlite3PagerSetBusyhandler(pBt->pPager, pHandler);
return SQLITE_OK;
}
@@ -1804,7 +1737,7 @@ int sqlite3BtreeSetBusyHandler(Btree *p, BusyHandler *pHandler){
*/
int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
BtShared *pBt = p->pBt;
- sqlite3pager_set_cachesize(pBt->pPager, mxPage);
+ sqlite3PagerSetCachesize(pBt->pPager, mxPage);
return SQLITE_OK;
}
@@ -1819,7 +1752,7 @@ int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
int sqlite3BtreeSetSafetyLevel(Btree *p, int level, int fullSync){
BtShared *pBt = p->pBt;
- sqlite3pager_set_safety_level(pBt->pPager, level, fullSync);
+ sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync);
return SQLITE_OK;
}
#endif
@@ -1831,7 +1764,7 @@ int sqlite3BtreeSetSafetyLevel(Btree *p, int level, int fullSync){
int sqlite3BtreeSyncDisabled(Btree *p){
BtShared *pBt = p->pBt;
assert( pBt && pBt->pPager );
- return sqlite3pager_nosync(pBt->pPager);
+ return sqlite3PagerNosync(pBt->pPager);
}
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
@@ -1862,7 +1795,7 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve){
((pageSize-1)&pageSize)==0 ){
assert( (pageSize & 7)==0 );
assert( !pBt->pPage1 && !pBt->pCursor );
- pBt->pageSize = sqlite3pager_set_pagesize(pBt->pPager, pageSize);
+ pBt->pageSize = sqlite3PagerSetPagesize(pBt->pPager, pageSize);
}
pBt->usableSize = pBt->pageSize - nReserve;
return SQLITE_OK;
@@ -1918,14 +1851,13 @@ int sqlite3BtreeGetAutoVacuum(Btree *p){
** SQLITE_OK is returned on success. If the file is not a
** well-formed database file, then SQLITE_CORRUPT is returned.
** SQLITE_BUSY is returned if the database is locked. SQLITE_NOMEM
-** is returned if we run out of memory. SQLITE_PROTOCOL is returned
-** if there is a locking protocol violation.
+** is returned if we run out of memory.
*/
static int lockBtree(BtShared *pBt){
int rc, pageSize;
MemPage *pPage1;
if( pBt->pPage1 ) return SQLITE_OK;
- rc = getPage(pBt, 1, &pPage1);
+ rc = getPage(pBt, 1, &pPage1, 0);
if( rc!=SQLITE_OK ) return rc;
@@ -1933,7 +1865,7 @@ static int lockBtree(BtShared *pBt){
** a valid database file.
*/
rc = SQLITE_NOTADB;
- if( sqlite3pager_pagecount(pBt->pPager)>0 ){
+ if( sqlite3PagerPagecount(pBt->pPager)>0 ){
u8 *page1 = pPage1->aData;
if( memcmp(page1, zMagicHeader, 16)!=0 ){
goto page1_init_failed;
@@ -1942,7 +1874,7 @@ static int lockBtree(BtShared *pBt){
goto page1_init_failed;
}
pageSize = get2byte(&page1[16]);
- if( ((pageSize-1)&pageSize)!=0 ){
+ if( ((pageSize-1)&pageSize)!=0 || pageSize<512 ){
goto page1_init_failed;
}
assert( (pageSize & 7)==0 );
@@ -2022,13 +1954,15 @@ static int lockBtreeWithRetry(Btree *pRef){
*/
static void unlockBtreeIfUnused(BtShared *pBt){
if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){
- if( pBt->pPage1->aData==0 ){
- MemPage *pPage = pBt->pPage1;
- pPage->aData = &((u8*)pPage)[-pBt->pageSize];
- pPage->pBt = pBt;
- pPage->pgno = 1;
+ if( sqlite3PagerRefcount(pBt->pPager)>=1 ){
+ if( pBt->pPage1->aData==0 ){
+ MemPage *pPage = pBt->pPage1;
+ pPage->aData = &((u8*)pPage)[-pBt->pageSize];
+ pPage->pBt = pBt;
+ pPage->pgno = 1;
+ }
+ releasePage(pBt->pPage1);
}
- releasePage(pBt->pPage1);
pBt->pPage1 = 0;
pBt->inStmt = 0;
}
@@ -2042,11 +1976,11 @@ static int newDatabase(BtShared *pBt){
MemPage *pP1;
unsigned char *data;
int rc;
- if( sqlite3pager_pagecount(pBt->pPager)>0 ) return SQLITE_OK;
+ if( sqlite3PagerPagecount(pBt->pPager)>0 ) return SQLITE_OK;
pP1 = pBt->pPage1;
assert( pP1!=0 );
data = pP1->aData;
- rc = sqlite3pager_write(data);
+ rc = sqlite3PagerWrite(pP1->pDbPage);
if( rc ) return rc;
memcpy(data, zMagicHeader, sizeof(zMagicHeader));
assert( sizeof(zMagicHeader)==16 );
@@ -2136,7 +2070,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
}
if( rc==SQLITE_OK && wrflag ){
- rc = sqlite3pager_begin(pBt->pPage1->aData, wrflag>1);
+ rc = sqlite3PagerBegin(pBt->pPage1->pDbPage, wrflag>1);
if( rc==SQLITE_OK ){
rc = newDatabase(pBt);
}
@@ -2292,7 +2226,7 @@ static int relocatePage(
/* Move page iDbPage from it's current location to page number iFreePage */
TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n",
iDbPage, iFreePage, iPtrPage, eType));
- rc = sqlite3pager_movepage(pPager, pDbPage->aData, iFreePage);
+ rc = sqlite3PagerMovepage(pPager, pDbPage->pDbPage, iFreePage);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -2326,11 +2260,11 @@ static int relocatePage(
** iPtrPage.
*/
if( eType!=PTRMAP_ROOTPAGE ){
- rc = getPage(pBt, iPtrPage, &pPtrPage);
+ rc = getPage(pBt, iPtrPage, &pPtrPage, 0);
if( rc!=SQLITE_OK ){
return rc;
}
- rc = sqlite3pager_write(pPtrPage->aData);
+ rc = sqlite3PagerWrite(pPtrPage->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(pPtrPage);
return rc;
@@ -2345,13 +2279,18 @@ static int relocatePage(
}
/* Forward declaration required by autoVacuumCommit(). */
-static int allocatePage(BtShared *, MemPage **, Pgno *, Pgno, u8);
+static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8);
/*
-** This routine is called prior to sqlite3pager_commit when a transaction
+** This routine is called prior to sqlite3PagerCommit when a transaction
** is commited for an auto-vacuum database.
+**
+** If SQLITE_OK is returned, then *pnTrunc is set to the number of pages
+** the database file should be truncated to during the commit process.
+** i.e. the database has been reorganized so that only the first *pnTrunc
+** pages are in use.
*/
-static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){
+static int autoVacuumCommit(BtShared *pBt, Pgno *pnTrunc){
Pager *pPager = pBt->pPager;
Pgno nFreeList; /* Number of pages remaining on the free-list. */
int nPtrMap; /* Number of pointer-map pages deallocated */
@@ -2367,11 +2306,11 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){
MemPage *pFreeMemPage = 0; /* "" */
#ifndef NDEBUG
- int nRef = sqlite3pager_refcount(pPager);
+ int nRef = sqlite3PagerRefcount(pPager);
#endif
assert( pBt->autoVacuum );
- if( PTRMAP_ISPAGE(pBt, sqlite3pager_pagecount(pPager)) ){
+ if( PTRMAP_ISPAGE(pBt, sqlite3PagerPagecount(pPager)) ){
return SQLITE_CORRUPT_BKPT;
}
@@ -2380,7 +2319,7 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){
*/
nFreeList = get4byte(&pBt->pPage1->aData[36]);
if( nFreeList==0 ){
- *nTrunc = 0;
+ *pnTrunc = 0;
return SQLITE_OK;
}
@@ -2393,7 +2332,7 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){
** be required, less 1 if the pending-byte page was part of the database
** but is not after the truncation.
**/
- origSize = sqlite3pager_pagecount(pPager);
+ origSize = sqlite3PagerPagecount(pPager);
if( origSize==PENDING_BYTE_PAGE(pBt) ){
origSize--;
}
@@ -2430,19 +2369,19 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){
if( eType==PTRMAP_FREEPAGE ){
continue;
}
- rc = getPage(pBt, iDbPage, &pDbMemPage);
+ rc = getPage(pBt, iDbPage, &pDbMemPage, 0);
if( rc!=SQLITE_OK ) goto autovacuum_out;
/* Find the next page in the free-list that is not already at the end
** of the file. A page can be pulled off the free list using the
- ** allocatePage() routine.
+ ** allocateBtreePage() routine.
*/
do{
if( pFreeMemPage ){
releasePage(pFreeMemPage);
pFreeMemPage = 0;
}
- rc = allocatePage(pBt, &pFreeMemPage, &iFreePage, 0, 0);
+ rc = allocateBtreePage(pBt, &pFreeMemPage, &iFreePage, 0, 0);
if( rc!=SQLITE_OK ){
releasePage(pDbMemPage);
goto autovacuum_out;
@@ -2467,29 +2406,81 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){
** truncate the database file to finSize pages and consider the
** free-list empty.
*/
- rc = sqlite3pager_write(pBt->pPage1->aData);
+ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
if( rc!=SQLITE_OK ) goto autovacuum_out;
put4byte(&pBt->pPage1->aData[32], 0);
put4byte(&pBt->pPage1->aData[36], 0);
- *nTrunc = finSize;
+ *pnTrunc = finSize;
assert( finSize!=PENDING_BYTE_PAGE(pBt) );
autovacuum_out:
- assert( nRef==sqlite3pager_refcount(pPager) );
+ assert( nRef==sqlite3PagerRefcount(pPager) );
if( rc!=SQLITE_OK ){
- sqlite3pager_rollback(pPager);
+ sqlite3PagerRollback(pPager);
}
return rc;
}
#endif
/*
+** This routine does the first phase of a two-phase commit. This routine
+** causes a rollback journal to be created (if it does not already exist)
+** and populated with enough information so that if a power loss occurs
+** the database can be restored to its original state by playing back
+** the journal. Then the contents of the journal are flushed out to
+** the disk. After the journal is safely on oxide, the changes to the
+** database are written into the database file and flushed to oxide.
+** At the end of this call, the rollback journal still exists on the
+** disk and we are still holding all locks, so the transaction has not
+** committed. See sqlite3BtreeCommit() for the second phase of the
+** commit process.
+**
+** This call is a no-op if no write-transaction is currently active on pBt.
+**
+** Otherwise, sync the database file for the btree pBt. zMaster points to
+** the name of a master journal file that should be written into the
+** individual journal file, or is NULL, indicating no master journal file
+** (single database transaction).
+**
+** When this is called, the master journal should already have been
+** created, populated with this journal pointer and synced to disk.
+**
+** Once this is routine has returned, the only thing required to commit
+** the write-transaction for this database file is to delete the journal.
+*/
+int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
+ int rc = SQLITE_OK;
+ if( p->inTrans==TRANS_WRITE ){
+ BtShared *pBt = p->pBt;
+ Pgno nTrunc = 0;
+#ifndef SQLITE_OMIT_AUTOVACUUM
+ if( pBt->autoVacuum ){
+ rc = autoVacuumCommit(pBt, &nTrunc);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ }
+#endif
+ rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, nTrunc);
+ }
+ return rc;
+}
+
+/*
** Commit the transaction currently in progress.
**
+** This routine implements the second phase of a 2-phase commit. The
+** sqlite3BtreeSync() routine does the first phase and should be invoked
+** prior to calling this routine. The sqlite3BtreeSync() routine did
+** all the work of writing information out to disk and flushing the
+** contents so that they are written onto the disk platter. All this
+** routine has to do is delete or truncate the rollback journal
+** (which causes the transaction to commit) and drop locks.
+**
** This will release the write lock on the database file. If there
** are no active cursors, it also releases the read lock.
*/
-int sqlite3BtreeCommit(Btree *p){
+int sqlite3BtreeCommitPhaseTwo(Btree *p){
BtShared *pBt = p->pBt;
btreeIntegrity(p);
@@ -2501,7 +2492,7 @@ int sqlite3BtreeCommit(Btree *p){
int rc;
assert( pBt->inTransaction==TRANS_WRITE );
assert( pBt->nTransaction>0 );
- rc = sqlite3pager_commit(pBt->pPager);
+ rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -2532,6 +2523,18 @@ int sqlite3BtreeCommit(Btree *p){
return SQLITE_OK;
}
+/*
+** Do both phases of a commit.
+*/
+int sqlite3BtreeCommit(Btree *p){
+ int rc;
+ rc = sqlite3BtreeCommitPhaseOne(p, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3BtreeCommitPhaseTwo(p);
+ }
+ return rc;
+}
+
#ifndef NDEBUG
/*
** Return the number of write-cursors open on this handle. This is for use
@@ -2548,7 +2551,7 @@ static int countWriteCursors(BtShared *pBt){
}
#endif
-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
+#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
/*
** Print debugging information about all cursors to standard output.
*/
@@ -2606,7 +2609,7 @@ int sqlite3BtreeRollback(Btree *p){
int rc2;
assert( TRANS_WRITE==pBt->inTransaction );
- rc2 = sqlite3pager_rollback(pBt->pPager);
+ rc2 = sqlite3PagerRollback(pBt->pPager);
if( rc2!=SQLITE_OK ){
rc = rc2;
}
@@ -2614,7 +2617,7 @@ int sqlite3BtreeRollback(Btree *p){
/* The rollback may have destroyed the pPage1->aData value. So
** call getPage() on page 1 again to make sure pPage1->aData is
** set correctly. */
- if( getPage(pBt, 1, &pPage1)==SQLITE_OK ){
+ if( getPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){
releasePage(pPage1);
}
assert( countWriteCursors(pBt)==0 );
@@ -2659,7 +2662,7 @@ int sqlite3BtreeBeginStmt(Btree *p){
return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
}
assert( pBt->inTransaction==TRANS_WRITE );
- rc = pBt->readOnly ? SQLITE_OK : sqlite3pager_stmt_begin(pBt->pPager);
+ rc = pBt->readOnly ? SQLITE_OK : sqlite3PagerStmtBegin(pBt->pPager);
pBt->inStmt = 1;
return rc;
}
@@ -2673,7 +2676,7 @@ int sqlite3BtreeCommitStmt(Btree *p){
int rc;
BtShared *pBt = p->pBt;
if( pBt->inStmt && !pBt->readOnly ){
- rc = sqlite3pager_stmt_commit(pBt->pPager);
+ rc = sqlite3PagerStmtCommit(pBt->pPager);
}else{
rc = SQLITE_OK;
}
@@ -2694,7 +2697,7 @@ int sqlite3BtreeRollbackStmt(Btree *p){
BtShared *pBt = p->pBt;
sqlite3MallocDisallow();
if( pBt->inStmt && !pBt->readOnly ){
- rc = sqlite3pager_stmt_rollback(pBt->pPager);
+ rc = sqlite3PagerStmtRollback(pBt->pPager);
assert( countWriteCursors(pBt)==0 );
pBt->inStmt = 0;
}
@@ -2732,25 +2735,16 @@ static int dfltCompare(
**
** 1: The cursor must have been opened with wrFlag==1
**
-** 2: No other cursors may be open with wrFlag==0 on the same table
+** 2: Other database connections that share the same pager cache
+** but which are not in the READ_UNCOMMITTED state may not have
+** cursors open with wrFlag==0 on the same table. Otherwise
+** the changes made by this write cursor would be visible to
+** the read cursors in the other database connection.
**
** 3: The database must be writable (not on read-only media)
**
** 4: There must be an active transaction.
**
-** Condition 2 warrants further discussion. If any cursor is opened
-** on a table with wrFlag==0, that prevents all other cursors from
-** writing to that table. This is a kind of "read-lock". When a cursor
-** is opened with wrFlag==0 it is guaranteed that the table will not
-** change as long as the cursor is open. This allows the cursor to
-** do a sequential scan of the table without having to worry about
-** entries being inserted or deleted during the scan. Cursors should
-** be opened with wrFlag==0 only if this read-lock property is needed.
-** That is to say, cursors should be opened with wrFlag==0 only if they
-** intend to use the sqlite3BtreeNext() system call. All other cursors
-** should be opened with wrFlag==1 even if they never really intend
-** to write.
-**
** No checking is done to make sure that page iTable really is the
** root page of a b-tree. If it is not, then the cursor acquired
** will not work correctly.
@@ -2778,7 +2772,7 @@ int sqlite3BtreeCursor(
if( pBt->readOnly ){
return SQLITE_READONLY;
}
- if( checkReadLocks(pBt, iTable, 0) ){
+ if( checkReadLocks(p, iTable, 0) ){
return SQLITE_LOCKED;
}
}
@@ -2795,7 +2789,7 @@ int sqlite3BtreeCursor(
goto create_cursor_exception;
}
pCur->pgnoRoot = (Pgno)iTable;
- if( iTable==1 && sqlite3pager_pagecount(pBt->pPager)==0 ){
+ if( iTable==1 && sqlite3PagerPagecount(pBt->pPager)==0 ){
rc = SQLITE_EMPTY;
goto create_cursor_exception;
}
@@ -2850,7 +2844,7 @@ void sqlite3BtreeSetCompare(
*/
int sqlite3BtreeCloseCursor(BtCursor *pCur){
BtShared *pBt = pCur->pBtree->pBt;
- restoreOrClearCursorPosition(pCur, 0);
+ clearCursorPosition(pCur);
if( pCur->pPrev ){
pCur->pPrev->pNext = pCur->pNext;
}else{
@@ -2874,7 +2868,7 @@ static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){
pTempCur->pNext = 0;
pTempCur->pPrev = 0;
if( pTempCur->pPage ){
- sqlite3pager_ref(pTempCur->pPage->aData);
+ sqlite3PagerRef(pTempCur->pPage->pDbPage);
}
}
@@ -2884,7 +2878,7 @@ static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){
*/
static void releaseTempCursor(BtCursor *pCur){
if( pCur->pPage ){
- sqlite3pager_unref(pCur->pPage->aData);
+ sqlite3PagerUnref(pCur->pPage->pDbPage);
}
}
@@ -2917,7 +2911,7 @@ static void getCellInfo(BtCursor *pCur){
** itself, not the number of bytes in the key.
*/
int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
- int rc = restoreOrClearCursorPosition(pCur, 1);
+ int rc = restoreOrClearCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
if( pCur->eState==CURSOR_INVALID ){
@@ -2938,7 +2932,7 @@ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
** the database is empty) then *pSize is set to 0.
*/
int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
- int rc = restoreOrClearCursorPosition(pCur, 1);
+ int rc = restoreOrClearCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
if( pCur->eState==CURSOR_INVALID ){
@@ -2980,7 +2974,6 @@ static int getPayload(
assert( pCur->eState==CURSOR_VALID );
pBt = pCur->pBtree->pBt;
pPage = pCur->pPage;
- pageIntegrity(pPage);
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
getCellInfo(pCur);
aPayload = pCur->info.pCell + pCur->info.nHeader;
@@ -3015,10 +3008,12 @@ static int getPayload(
if( amt>0 ){
nextPage = get4byte(&aPayload[pCur->info.nLocal]);
while( amt>0 && nextPage ){
- rc = sqlite3pager_get(pBt->pPager, nextPage, (void**)&aPayload);
+ DbPage *pDbPage;
+ rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage);
if( rc!=0 ){
return rc;
}
+ aPayload = sqlite3PagerGetData(pDbPage);
nextPage = get4byte(aPayload);
if( offset<ovflSize ){
int a = amt;
@@ -3032,7 +3027,7 @@ static int getPayload(
}else{
offset -= ovflSize;
}
- sqlite3pager_unref(aPayload);
+ sqlite3PagerUnref(pDbPage);
}
}
@@ -3052,7 +3047,7 @@ static int getPayload(
** the available payload.
*/
int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
- int rc = restoreOrClearCursorPosition(pCur, 1);
+ int rc = restoreOrClearCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_VALID );
assert( pCur->pPage!=0 );
@@ -3076,7 +3071,7 @@ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
** the available payload.
*/
int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
- int rc = restoreOrClearCursorPosition(pCur, 1);
+ int rc = restoreOrClearCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_VALID );
assert( pCur->pPage!=0 );
@@ -3118,7 +3113,6 @@ static const unsigned char *fetchPayload(
assert( pCur!=0 && pCur->pPage!=0 );
assert( pCur->eState==CURSOR_VALID );
pPage = pCur->pPage;
- pageIntegrity(pPage);
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
getCellInfo(pCur);
aPayload = pCur->info.pCell;
@@ -3180,7 +3174,6 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
assert( pCur->eState==CURSOR_VALID );
rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
if( rc ) return rc;
- pageIntegrity(pNewPage);
pNewPage->idxParent = pCur->idx;
pOldPage = pCur->pPage;
pOldPage->idxShift = 0;
@@ -3228,12 +3221,10 @@ static void moveToParent(BtCursor *pCur){
pPage = pCur->pPage;
assert( pPage!=0 );
assert( !isRootPage(pPage) );
- pageIntegrity(pPage);
pParent = pPage->pParent;
assert( pParent!=0 );
- pageIntegrity(pParent);
idxParent = pPage->idxParent;
- sqlite3pager_ref(pParent->aData);
+ sqlite3PagerRef(pParent->pDbPage);
releasePage(pPage);
pCur->pPage = pParent;
pCur->info.nSize = 0;
@@ -3249,7 +3240,9 @@ static int moveToRoot(BtCursor *pCur){
int rc = SQLITE_OK;
BtShared *pBt = pCur->pBtree->pBt;
- restoreOrClearCursorPosition(pCur, 0);
+ if( pCur->eState==CURSOR_REQUIRESEEK ){
+ clearCursorPosition(pCur);
+ }
pRoot = pCur->pPage;
if( pRoot && pRoot->pgno==pCur->pgnoRoot ){
assert( pRoot->isInit );
@@ -3261,7 +3254,6 @@ static int moveToRoot(BtCursor *pCur){
return rc;
}
releasePage(pCur->pPage);
- pageIntegrity(pRoot);
pCur->pPage = pRoot;
}
pCur->idx = 0;
@@ -3392,20 +3384,24 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
** *pRes>0 The cursor is left pointing at an entry that
** is larger than pKey.
*/
-int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
+int sqlite3BtreeMoveto(
+ BtCursor *pCur, /* The cursor to be moved */
+ const void *pKey, /* The key content for indices. Not used by tables */
+ i64 nKey, /* Size of pKey. Or the key for tables */
+ int biasRight, /* If true, bias the search to the high end */
+ int *pRes /* Search result flag */
+){
int rc;
- int tryRightmost;
rc = moveToRoot(pCur);
if( rc ) return rc;
assert( pCur->pPage );
assert( pCur->pPage->isInit );
- tryRightmost = pCur->pPage->intKey;
if( pCur->eState==CURSOR_INVALID ){
*pRes = -1;
assert( pCur->pPage->nCell==0 );
return SQLITE_OK;
}
- for(;;){
+ for(;;){
int lwr, upr;
Pgno chldPg;
MemPage *pPage = pCur->pPage;
@@ -3415,17 +3411,17 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
if( !pPage->intKey && pKey==0 ){
return SQLITE_CORRUPT_BKPT;
}
- pageIntegrity(pPage);
- while( lwr<=upr ){
+ if( biasRight ){
+ pCur->idx = upr;
+ }else{
+ pCur->idx = (upr+lwr)/2;
+ }
+ if( lwr<=upr ) for(;;){
void *pCellKey;
i64 nCellKey;
- pCur->idx = (lwr+upr)/2;
pCur->info.nSize = 0;
if( pPage->intKey ){
u8 *pCell;
- if( tryRightmost ){
- pCur->idx = upr;
- }
pCell = findCell(pPage, pCur->idx) + pPage->childPtrSize;
if( pPage->hasData ){
u32 dummy;
@@ -3436,7 +3432,6 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
c = -1;
}else if( nCellKey>nKey ){
c = +1;
- tryRightmost = 0;
}else{
c = 0;
}
@@ -3470,6 +3465,10 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
}else{
upr = pCur->idx-1;
}
+ if( lwr>upr ){
+ break;
+ }
+ pCur->idx = (lwr+upr)/2;
}
assert( lwr==upr+1 );
assert( pPage->isInit );
@@ -3521,10 +3520,18 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
MemPage *pPage;
#ifndef SQLITE_OMIT_SHARED_CACHE
- rc = restoreOrClearCursorPosition(pCur, 1);
+ rc = restoreOrClearCursorPosition(pCur);
if( rc!=SQLITE_OK ){
return rc;
}
+#endif
+ assert( pRes!=0 );
+ pPage = pCur->pPage;
+ if( CURSOR_INVALID==pCur->eState ){
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+#ifndef SQLITE_OMIT_SHARED_CACHE
if( pCur->skip>0 ){
pCur->skip = 0;
*pRes = 0;
@@ -3533,12 +3540,6 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
pCur->skip = 0;
#endif
- assert( pRes!=0 );
- pPage = pCur->pPage;
- if( CURSOR_INVALID==pCur->eState ){
- *pRes = 1;
- return SQLITE_OK;
- }
assert( pPage->isInit );
assert( pCur->idx<pPage->nCell );
@@ -3589,10 +3590,16 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
MemPage *pPage;
#ifndef SQLITE_OMIT_SHARED_CACHE
- rc = restoreOrClearCursorPosition(pCur, 1);
+ rc = restoreOrClearCursorPosition(pCur);
if( rc!=SQLITE_OK ){
return rc;
}
+#endif
+ if( CURSOR_INVALID==pCur->eState ){
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+#ifndef SQLITE_OMIT_SHARED_CACHE
if( pCur->skip<0 ){
pCur->skip = 0;
*pRes = 0;
@@ -3601,11 +3608,6 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
pCur->skip = 0;
#endif
- if( CURSOR_INVALID==pCur->eState ){
- *pRes = 1;
- return SQLITE_OK;
- }
-
pPage = pCur->pPage;
assert( pPage->isInit );
assert( pCur->idx>=0 );
@@ -3639,14 +3641,14 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
/*
** Allocate a new page from the database file.
**
-** The new page is marked as dirty. (In other words, sqlite3pager_write()
+** The new page is marked as dirty. (In other words, sqlite3PagerWrite()
** has already been called on the new page.) The new page has also
** been referenced and the calling routine is responsible for calling
-** sqlite3pager_unref() on the new page when it is done.
+** sqlite3PagerUnref() on the new page when it is done.
**
** SQLITE_OK is returned on success. Any other return value indicates
** an error. *ppPage and *pPgno are undefined in the event of an error.
-** Do not invoke sqlite3pager_unref() on *ppPage if an error is returned.
+** Do not invoke sqlite3PagerUnref() on *ppPage if an error is returned.
**
** If the "nearby" parameter is not 0, then a (feeble) effort is made to
** locate a page close to the page number "nearby". This can be used in an
@@ -3657,7 +3659,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
** anywhere on the free-list, then it is guarenteed to be returned. This
** is only used by auto-vacuum databases when allocating a new table.
*/
-static int allocatePage(
+static int allocateBtreePage(
BtShared *pBt,
MemPage **ppPage,
Pgno *pPgno,
@@ -3668,14 +3670,14 @@ static int allocatePage(
int rc;
int n; /* Number of pages on the freelist */
int k; /* Number of leaves on the trunk of the freelist */
+ MemPage *pTrunk = 0;
+ MemPage *pPrevTrunk = 0;
pPage1 = pBt->pPage1;
n = get4byte(&pPage1->aData[36]);
if( n>0 ){
/* There are pages on the freelist. Reuse one of those pages. */
- MemPage *pTrunk = 0;
Pgno iTrunk;
- MemPage *pPrevTrunk = 0;
u8 searchList = 0; /* If the free-list must be searched for 'nearby' */
/* If the 'exact' parameter was true and a query of the pointer-map
@@ -3699,7 +3701,7 @@ static int allocatePage(
/* Decrement the free-list count by 1. Set iTrunk to the index of the
** first free-list trunk page. iPrevTrunk is initially 1.
*/
- rc = sqlite3pager_write(pPage1->aData);
+ rc = sqlite3PagerWrite(pPage1->pDbPage);
if( rc ) return rc;
put4byte(&pPage1->aData[36], n-1);
@@ -3714,18 +3716,10 @@ static int allocatePage(
}else{
iTrunk = get4byte(&pPage1->aData[32]);
}
- rc = getPage(pBt, iTrunk, &pTrunk);
- if( rc ){
- releasePage(pPrevTrunk);
- return rc;
- }
-
- /* TODO: This should move to after the loop? */
- rc = sqlite3pager_write(pTrunk->aData);
+ rc = getPage(pBt, iTrunk, &pTrunk, 0);
if( rc ){
- releasePage(pTrunk);
- releasePage(pPrevTrunk);
- return rc;
+ pTrunk = 0;
+ goto end_allocate_page;
}
k = get4byte(&pTrunk->aData[4]);
@@ -3734,6 +3728,10 @@ static int allocatePage(
** So extract the trunk page itself and use it as the newly
** allocated page */
assert( pPrevTrunk==0 );
+ rc = sqlite3PagerWrite(pTrunk->pDbPage);
+ if( rc ){
+ goto end_allocate_page;
+ }
*pPgno = iTrunk;
memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
*ppPage = pTrunk;
@@ -3741,7 +3739,8 @@ static int allocatePage(
TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1));
}else if( k>pBt->usableSize/4 - 8 ){
/* Value of k is out of range. Database corruption */
- return SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_BKPT;
+ goto end_allocate_page;
#ifndef SQLITE_OMIT_AUTOVACUUM
}else if( searchList && nearby==iTrunk ){
/* The list is being searched and this trunk page is the page
@@ -3750,6 +3749,10 @@ static int allocatePage(
assert( *pPgno==iTrunk );
*ppPage = pTrunk;
searchList = 0;
+ rc = sqlite3PagerWrite(pTrunk->pDbPage);
+ if( rc ){
+ goto end_allocate_page;
+ }
if( k==0 ){
if( !pPrevTrunk ){
memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
@@ -3763,28 +3766,28 @@ static int allocatePage(
*/
MemPage *pNewTrunk;
Pgno iNewTrunk = get4byte(&pTrunk->aData[8]);
- rc = getPage(pBt, iNewTrunk, &pNewTrunk);
+ rc = getPage(pBt, iNewTrunk, &pNewTrunk, 0);
if( rc!=SQLITE_OK ){
- releasePage(pTrunk);
- releasePage(pPrevTrunk);
- return rc;
+ goto end_allocate_page;
}
- rc = sqlite3pager_write(pNewTrunk->aData);
+ rc = sqlite3PagerWrite(pNewTrunk->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(pNewTrunk);
- releasePage(pTrunk);
- releasePage(pPrevTrunk);
- return rc;
+ goto end_allocate_page;
}
memcpy(&pNewTrunk->aData[0], &pTrunk->aData[0], 4);
put4byte(&pNewTrunk->aData[4], k-1);
memcpy(&pNewTrunk->aData[8], &pTrunk->aData[12], (k-1)*4);
+ releasePage(pNewTrunk);
if( !pPrevTrunk ){
put4byte(&pPage1->aData[32], iNewTrunk);
}else{
+ rc = sqlite3PagerWrite(pPrevTrunk->pDbPage);
+ if( rc ){
+ goto end_allocate_page;
+ }
put4byte(&pPrevTrunk->aData[0], iNewTrunk);
}
- releasePage(pNewTrunk);
}
pTrunk = 0;
TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1));
@@ -3794,6 +3797,10 @@ static int allocatePage(
int closest;
Pgno iPage;
unsigned char *aData = pTrunk->aData;
+ rc = sqlite3PagerWrite(pTrunk->pDbPage);
+ if( rc ){
+ goto end_allocate_page;
+ }
if( nearby>0 ){
int i, dist;
closest = 0;
@@ -3814,7 +3821,7 @@ static int allocatePage(
iPage = get4byte(&aData[8+closest*4]);
if( !searchList || iPage==nearby ){
*pPgno = iPage;
- if( *pPgno>sqlite3pager_pagecount(pBt->pPager) ){
+ if( *pPgno>sqlite3PagerPagecount(pBt->pPager) ){
/* Free page off the end of the file */
return SQLITE_CORRUPT_BKPT;
}
@@ -3825,10 +3832,10 @@ static int allocatePage(
memcpy(&aData[8+closest*4], &aData[4+k*4], 4);
}
put4byte(&aData[4], k-1);
- rc = getPage(pBt, *pPgno, ppPage);
+ rc = getPage(pBt, *pPgno, ppPage, 1);
if( rc==SQLITE_OK ){
- sqlite3pager_dont_rollback((*ppPage)->aData);
- rc = sqlite3pager_write((*ppPage)->aData);
+ sqlite3PagerDontRollback((*ppPage)->pDbPage);
+ rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(*ppPage);
}
@@ -3837,12 +3844,12 @@ static int allocatePage(
}
}
releasePage(pPrevTrunk);
+ pPrevTrunk = 0;
}while( searchList );
- releasePage(pTrunk);
}else{
/* There are no pages on the freelist, so create a new page at the
** end of the file */
- *pPgno = sqlite3pager_pagecount(pBt->pPager) + 1;
+ *pPgno = sqlite3PagerPagecount(pBt->pPager) + 1;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, *pPgno) ){
@@ -3857,9 +3864,9 @@ static int allocatePage(
#endif
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
- rc = getPage(pBt, *pPgno, ppPage);
+ rc = getPage(pBt, *pPgno, ppPage, 0);
if( rc ) return rc;
- rc = sqlite3pager_write((*ppPage)->aData);
+ rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(*ppPage);
}
@@ -3867,13 +3874,17 @@ static int allocatePage(
}
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
+
+end_allocate_page:
+ releasePage(pTrunk);
+ releasePage(pPrevTrunk);
return rc;
}
/*
** Add a page of the database file to the freelist.
**
-** sqlite3pager_unref() is NOT called for pPage.
+** sqlite3PagerUnref() is NOT called for pPage.
*/
static int freePage(MemPage *pPage){
BtShared *pBt = pPage->pBt;
@@ -3887,7 +3898,7 @@ static int freePage(MemPage *pPage){
pPage->pParent = 0;
/* Increment the free page count on pPage1 */
- rc = sqlite3pager_write(pPage1->aData);
+ rc = sqlite3PagerWrite(pPage1->pDbPage);
if( rc ) return rc;
n = get4byte(&pPage1->aData[36]);
put4byte(&pPage1->aData[36], n+1);
@@ -3896,7 +3907,7 @@ static int freePage(MemPage *pPage){
/* If the SQLITE_SECURE_DELETE compile-time option is enabled, then
** always fully overwrite deleted information with zeros.
*/
- rc = sqlite3pager_write(pPage->aData);
+ rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ) return rc;
memset(pPage->aData, 0, pPage->pBt->pageSize);
#endif
@@ -3913,7 +3924,7 @@ static int freePage(MemPage *pPage){
if( n==0 ){
/* This is the first free page */
- rc = sqlite3pager_write(pPage->aData);
+ rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ) return rc;
memset(pPage->aData, 0, 8);
put4byte(&pPage1->aData[32], pPage->pgno);
@@ -3922,13 +3933,13 @@ static int freePage(MemPage *pPage){
/* Other free pages already exist. Retrive the first trunk page
** of the freelist and find out how many leaves it has. */
MemPage *pTrunk;
- rc = getPage(pBt, get4byte(&pPage1->aData[32]), &pTrunk);
+ rc = getPage(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. */
- rc = sqlite3pager_write(pPage->aData);
+ rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ) return rc;
put4byte(pPage->aData, pTrunk->pgno);
put4byte(&pPage->aData[4], 0);
@@ -3937,13 +3948,14 @@ static int freePage(MemPage *pPage){
pPage->pgno, pTrunk->pgno));
}else{
/* Add the newly freed page as a leaf on the current trunk */
- rc = sqlite3pager_write(pTrunk->aData);
- if( rc ) return rc;
- put4byte(&pTrunk->aData[4], k+1);
- put4byte(&pTrunk->aData[8+k*4], pPage->pgno);
+ rc = sqlite3PagerWrite(pTrunk->pDbPage);
+ if( rc==SQLITE_OK ){
+ put4byte(&pTrunk->aData[4], k+1);
+ put4byte(&pTrunk->aData[8+k*4], pPage->pgno);
#ifndef SQLITE_SECURE_DELETE
- sqlite3pager_dont_write(pBt->pPager, pPage->pgno);
+ sqlite3PagerDontWrite(pPage->pDbPage);
#endif
+ }
TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno));
}
releasePage(pTrunk);
@@ -3959,22 +3971,29 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
CellInfo info;
Pgno ovflPgno;
int rc;
+ int nOvfl;
+ int ovflPageSize;
parseCellPtr(pPage, pCell, &info);
if( info.iOverflow==0 ){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
}
ovflPgno = get4byte(&pCell[info.iOverflow]);
- while( ovflPgno!=0 ){
+ ovflPageSize = pBt->usableSize - 4;
+ nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize;
+ assert( ovflPgno==0 || nOvfl>0 );
+ while( nOvfl-- ){
MemPage *pOvfl;
- if( ovflPgno>sqlite3pager_pagecount(pBt->pPager) ){
+ if( ovflPgno==0 || ovflPgno>sqlite3PagerPagecount(pBt->pPager) ){
return SQLITE_CORRUPT_BKPT;
}
- rc = getPage(pBt, ovflPgno, &pOvfl);
+ rc = getPage(pBt, ovflPgno, &pOvfl, nOvfl==0);
if( rc ) return rc;
- ovflPgno = get4byte(pOvfl->aData);
+ if( nOvfl ){
+ ovflPgno = get4byte(pOvfl->aData);
+ }
rc = freePage(pOvfl);
- sqlite3pager_unref(pOvfl->aData);
+ sqlite3PagerUnref(pOvfl->pDbPage);
if( rc ) return rc;
}
return SQLITE_OK;
@@ -4049,7 +4068,7 @@ static int fillInCell(
#ifndef SQLITE_OMIT_AUTOVACUUM
Pgno pgnoPtrmap = pgnoOvfl; /* Overflow page pointer-map entry page */
#endif
- rc = allocatePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl, 0);
+ rc = allocateBtreePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl, 0);
#ifndef SQLITE_OMIT_AUTOVACUUM
/* If the database supports auto-vacuum, and the second or subsequent
** overflow page is being allocated, add an entry to the pointer-map
@@ -4062,7 +4081,6 @@ static int fillInCell(
#endif
if( rc ){
releasePage(pToRelease);
- /* clearCell(pPage, pCell); */
return rc;
}
put4byte(pPrior, pgnoOvfl);
@@ -4099,24 +4117,24 @@ static int fillInCell(
*/
static int reparentPage(BtShared *pBt, Pgno pgno, MemPage *pNewParent, int idx){
MemPage *pThis;
- unsigned char *aData;
+ DbPage *pDbPage;
assert( pNewParent!=0 );
if( pgno==0 ) return SQLITE_OK;
assert( pBt->pPager!=0 );
- aData = sqlite3pager_lookup(pBt->pPager, pgno);
- if( aData ){
- pThis = (MemPage*)&aData[pBt->pageSize];
- assert( pThis->aData==aData );
+ pDbPage = sqlite3PagerLookup(pBt->pPager, pgno);
+ if( pDbPage ){
+ pThis = (MemPage *)sqlite3PagerGetExtra(pDbPage);
if( pThis->isInit ){
+ assert( pThis->aData==(sqlite3PagerGetData(pDbPage)) );
if( pThis->pParent!=pNewParent ){
- if( pThis->pParent ) sqlite3pager_unref(pThis->pParent->aData);
+ if( pThis->pParent ) sqlite3PagerUnref(pThis->pParent->pDbPage);
pThis->pParent = pNewParent;
- sqlite3pager_ref(pNewParent->aData);
+ sqlite3PagerRef(pNewParent->pDbPage);
}
pThis->idxParent = idx;
}
- sqlite3pager_unref(aData);
+ sqlite3PagerUnref(pDbPage);
}
#ifndef SQLITE_OMIT_AUTOVACUUM
@@ -4177,7 +4195,7 @@ static void dropCell(MemPage *pPage, int idx, int sz){
assert( idx>=0 && idx<pPage->nCell );
assert( sz==cellSize(pPage, idx) );
- assert( sqlite3pager_iswriteable(pPage->aData) );
+ assert( sqlite3PagerIswriteable(pPage->pDbPage) );
data = pPage->aData;
ptr = &data[pPage->cellOffset + 2*idx];
pc = get2byte(ptr);
@@ -4230,7 +4248,7 @@ static int insertCell(
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
assert( sz==cellSizePtr(pPage, pCell) );
- assert( sqlite3pager_iswriteable(pPage->aData) );
+ assert( sqlite3PagerIswriteable(pPage->pDbPage) );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip);
@@ -4267,7 +4285,6 @@ static int insertCell(
put2byte(&data[ins], idx);
put2byte(&data[hdr+3], pPage->nCell);
pPage->idxShift = 1;
- pageIntegrity(pPage);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pPage->pBt->autoVacuum ){
/* The cell may contain a pointer to an overflow page. If so, write
@@ -4275,6 +4292,7 @@ static int insertCell(
*/
CellInfo info;
parseCellPtr(pPage, pCell, &info);
+ assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload );
if( (info.nData+(pPage->intKey?0:info.nKey))>info.nLocal ){
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
int rc = ptrmapPut(pPage->pBt, pgnoOvfl, PTRMAP_OVERFLOW1, pPage->pgno);
@@ -4382,7 +4400,7 @@ static int balance_quick(MemPage *pPage, MemPage *pParent){
/* Allocate a new page. Insert the overflow cell from pPage
** into it. Then remove the overflow cell from pPage.
*/
- rc = allocatePage(pBt, &pNew, &pgnoNew, 0, 0);
+ rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -4394,7 +4412,7 @@ static int balance_quick(MemPage *pPage, MemPage *pParent){
/* Set the parent of the newly allocated page to pParent. */
pNew->pParent = pParent;
- sqlite3pager_ref(pParent->aData);
+ sqlite3PagerRef(pParent->pDbPage);
/* pPage is currently the right-child of pParent. Change this
** so that the right-child is the new page allocated above and
@@ -4519,11 +4537,11 @@ static int balance_nonroot(MemPage *pPage){
** Find the parent page.
*/
assert( pPage->isInit );
- assert( sqlite3pager_iswriteable(pPage->aData) );
+ assert( sqlite3PagerIswriteable(pPage->pDbPage) );
pBt = pPage->pBt;
pParent = pPage->pParent;
assert( pParent );
- if( SQLITE_OK!=(rc = sqlite3pager_write(pParent->aData)) ){
+ if( SQLITE_OK!=(rc = sqlite3PagerWrite(pParent->pDbPage)) ){
return rc;
}
TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno));
@@ -4561,7 +4579,7 @@ static int balance_nonroot(MemPage *pPage){
if( pParent->idxShift ){
Pgno pgno;
pgno = pPage->pgno;
- assert( pgno==sqlite3pager_pagenumber(pPage->aData) );
+ assert( pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
for(idx=0; idx<pParent->nCell; idx++){
if( get4byte(findCell(pParent, idx))==pgno ){
break;
@@ -4578,7 +4596,7 @@ static int balance_nonroot(MemPage *pPage){
** directly to balance_cleanup at any moment.
*/
nOld = nNew = 0;
- sqlite3pager_ref(pParent->aData);
+ sqlite3PagerRef(pParent->pDbPage);
/*
** Find sibling pages to pPage and the cells in pParent that divide
@@ -4822,15 +4840,16 @@ static int balance_nonroot(MemPage *pPage){
pNew = apNew[i] = apOld[i];
pgnoNew[i] = pgnoOld[i];
apOld[i] = 0;
- rc = sqlite3pager_write(pNew->aData);
+ rc = sqlite3PagerWrite(pNew->pDbPage);
+ nNew++;
if( rc ) goto balance_cleanup;
}else{
assert( i>0 );
- rc = allocatePage(pBt, &pNew, &pgnoNew[i], pgnoNew[i-1], 0);
+ rc = allocateBtreePage(pBt, &pNew, &pgnoNew[i], pgnoNew[i-1], 0);
if( rc ) goto balance_cleanup;
apNew[i] = pNew;
+ nNew++;
}
- nNew++;
zeroPage(pNew, pageFlags);
}
@@ -5007,8 +5026,6 @@ static int balance_nonroot(MemPage *pPage){
** But the parent page will always be initialized.
*/
assert( pParent->isInit );
- /* assert( pPage->isInit ); // No! pPage might have been added to freelist */
- /* pageIntegrity(pPage); // No! pPage might have been added to freelist */
rc = balance(pParent, 0);
/*
@@ -5067,8 +5084,8 @@ static int balance_shallower(MemPage *pPage){
*/
pgnoChild = get4byte(&pPage->aData[pPage->hdrOffset+8]);
assert( pgnoChild>0 );
- assert( pgnoChild<=sqlite3pager_pagecount(pPage->pBt->pPager) );
- rc = getPage(pPage->pBt, pgnoChild, &pChild);
+ assert( pgnoChild<=sqlite3PagerPagecount(pPage->pBt->pPager) );
+ rc = getPage(pPage->pBt, pgnoChild, &pChild, 0);
if( rc ) goto end_shallow_balance;
if( pPage->pgno==1 ){
rc = initPage(pChild, pPage);
@@ -5149,9 +5166,9 @@ static int balance_deeper(MemPage *pPage){
assert( pPage->pParent==0 );
assert( pPage->nOverflow>0 );
pBt = pPage->pBt;
- rc = allocatePage(pBt, &pChild, &pgnoChild, pPage->pgno, 0);
+ rc = allocateBtreePage(pBt, &pChild, &pgnoChild, pPage->pgno, 0);
if( rc ) return rc;
- assert( sqlite3pager_iswriteable(pChild->aData) );
+ assert( sqlite3PagerIswriteable(pChild->pDbPage) );
usableSize = pBt->usableSize;
data = pPage->aData;
hdr = pPage->hdrOffset;
@@ -5215,27 +5232,35 @@ static int balance(MemPage *pPage, int insert){
/*
** This routine checks all cursors that point to table pgnoRoot.
-** If any of those cursors other than pExclude were opened with
-** wrFlag==0 then this routine returns SQLITE_LOCKED. If all
-** cursors that point to pgnoRoot were opened with wrFlag==1
-** then this routine returns SQLITE_OK.
+** If any of those cursors were opened with wrFlag==0 in a different
+** database connection (a database connection that shares the pager
+** cache with the current connection) and that other connection
+** is not in the ReadUncommmitted state, then this routine returns
+** SQLITE_LOCKED.
**
** In addition to checking for read-locks (where a read-lock
** means a cursor opened with wrFlag==0) this routine also moves
-** all cursors other than pExclude so that they are pointing to the
-** first Cell on root page. This is necessary because an insert
+** all cursors write cursors so that they are pointing to the
+** first Cell on the root page. This is necessary because an insert
** or delete might change the number of cells on a page or delete
** a page entirely and we do not want to leave any cursors
** pointing to non-existant pages or cells.
*/
-static int checkReadLocks(BtShared *pBt, Pgno pgnoRoot, BtCursor *pExclude){
+static int checkReadLocks(Btree *pBtree, Pgno pgnoRoot, BtCursor *pExclude){
BtCursor *p;
+ BtShared *pBt = pBtree->pBt;
+ sqlite3 *db = pBtree->pSqlite;
for(p=pBt->pCursor; p; p=p->pNext){
- u32 flags = (p->pBtree->pSqlite ? p->pBtree->pSqlite->flags : 0);
- if( p->pgnoRoot!=pgnoRoot || p==pExclude ) continue;
- if( p->wrFlag==0 && flags&SQLITE_ReadUncommitted ) continue;
- if( p->wrFlag==0 ) return SQLITE_LOCKED;
- if( p->pPage->pgno!=p->pgnoRoot ){
+ if( p==pExclude ) continue;
+ if( p->eState!=CURSOR_VALID ) continue;
+ if( p->pgnoRoot!=pgnoRoot ) continue;
+ if( p->wrFlag==0 ){
+ sqlite3 *dbOther = p->pBtree->pSqlite;
+ if( dbOther==0 ||
+ (dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0) ){
+ return SQLITE_LOCKED;
+ }
+ }else if( p->pPage->pgno!=p->pgnoRoot ){
moveToRoot(p);
}
}
@@ -5254,7 +5279,8 @@ static int checkReadLocks(BtShared *pBt, Pgno pgnoRoot, BtCursor *pExclude){
int sqlite3BtreeInsert(
BtCursor *pCur, /* Insert data into the table of this cursor */
const void *pKey, i64 nKey, /* The key of the new record */
- const void *pData, int nData /* The data of the new record */
+ const void *pData, int nData, /* The data of the new record */
+ int appendBias /* True if this is likely an append */
){
int rc;
int loc;
@@ -5272,15 +5298,15 @@ int sqlite3BtreeInsert(
if( !pCur->wrFlag ){
return SQLITE_PERM; /* Cursor not open for writing */
}
- if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
+ if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
}
/* Save the positions of any other cursors open on this table */
- restoreOrClearCursorPosition(pCur, 0);
+ clearCursorPosition(pCur);
if(
SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) ||
- SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, nKey, &loc))
+ SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, nKey, appendBias, &loc))
){
return rc;
}
@@ -5292,7 +5318,7 @@ int sqlite3BtreeInsert(
pCur->pgnoRoot, nKey, nData, pPage->pgno,
loc==0 ? "overwrite" : "new entry"));
assert( pPage->isInit );
- rc = sqlite3pager_write(pPage->aData);
+ rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ) return rc;
newCell = sqliteMallocRaw( MX_CELL_SIZE(pBt) );
if( newCell==0 ) return SQLITE_NOMEM;
@@ -5354,19 +5380,19 @@ int sqlite3BtreeDelete(BtCursor *pCur){
if( !pCur->wrFlag ){
return SQLITE_PERM; /* Did not open this cursor for writing */
}
- if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
+ if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
}
/* Restore the current cursor position (a no-op if the cursor is not in
** CURSOR_REQUIRESEEK state) and save the positions of any other cursors
- ** open on the same table. Then call sqlite3pager_write() on the page
+ ** open on the same table. Then call sqlite3PagerWrite() on the page
** that the entry will be deleted from.
*/
if(
- (rc = restoreOrClearCursorPosition(pCur, 1))!=0 ||
+ (rc = restoreOrClearCursorPosition(pCur))!=0 ||
(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur))!=0 ||
- (rc = sqlite3pager_write(pPage->aData))!=0
+ (rc = sqlite3PagerWrite(pPage->pDbPage))!=0
){
return rc;
}
@@ -5400,13 +5426,8 @@ int sqlite3BtreeDelete(BtCursor *pCur){
assert( !pPage->leafData );
getTempCursor(pCur, &leafCur);
rc = sqlite3BtreeNext(&leafCur, &notUsed);
- if( rc!=SQLITE_OK ){
- if( rc!=SQLITE_NOMEM ){
- rc = SQLITE_CORRUPT_BKPT;
- }
- }
if( rc==SQLITE_OK ){
- rc = sqlite3pager_write(leafCur.pPage->aData);
+ rc = sqlite3PagerWrite(leafCur.pPage->pDbPage);
}
if( rc==SQLITE_OK ){
TRACE(("DELETE: table=%d delete internal from %d replace from leaf %d\n",
@@ -5477,7 +5498,7 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
}
#ifdef SQLITE_OMIT_AUTOVACUUM
- rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1, 0);
+ rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0);
if( rc ) return rc;
#else
if( pBt->autoVacuum ){
@@ -5505,17 +5526,25 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
** be moved to the allocated page (unless the allocated page happens
** to reside at pgnoRoot).
*/
- rc = allocatePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, 1);
+ rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, 1);
if( rc!=SQLITE_OK ){
return rc;
}
if( pgnoMove!=pgnoRoot ){
+ /* pgnoRoot is the page that will be used for the root-page of
+ ** the new table (assuming an error did not occur). But we were
+ ** allocated pgnoMove. If required (i.e. if it was not allocated
+ ** by extending the file), the current page at position pgnoMove
+ ** is already journaled.
+ */
u8 eType;
Pgno iPtrPage;
releasePage(pPageMove);
- rc = getPage(pBt, pgnoRoot, &pRoot);
+
+ /* Move the page currently at pgnoRoot to pgnoMove. */
+ rc = getPage(pBt, pgnoRoot, &pRoot, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -5526,21 +5555,23 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
}
assert( eType!=PTRMAP_ROOTPAGE );
assert( eType!=PTRMAP_FREEPAGE );
- rc = sqlite3pager_write(pRoot->aData);
+ rc = sqlite3PagerWrite(pRoot->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(pRoot);
return rc;
}
rc = relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove);
releasePage(pRoot);
+
+ /* Obtain the page at pgnoRoot */
if( rc!=SQLITE_OK ){
return rc;
}
- rc = getPage(pBt, pgnoRoot, &pRoot);
+ rc = getPage(pBt, pgnoRoot, &pRoot, 0);
if( rc!=SQLITE_OK ){
return rc;
}
- rc = sqlite3pager_write(pRoot->aData);
+ rc = sqlite3PagerWrite(pRoot->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(pRoot);
return rc;
@@ -5562,13 +5593,13 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
}
}else{
- rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1, 0);
+ rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0);
if( rc ) return rc;
}
#endif
- assert( sqlite3pager_iswriteable(pRoot->aData) );
+ assert( sqlite3PagerIswriteable(pRoot->pDbPage) );
zeroPage(pRoot, flags | PTF_LEAF);
- sqlite3pager_unref(pRoot->aData);
+ sqlite3PagerUnref(pRoot->pDbPage);
*piTable = (int)pgnoRoot;
return SQLITE_OK;
}
@@ -5588,14 +5619,12 @@ static int clearDatabasePage(
unsigned char *pCell;
int i;
- if( pgno>sqlite3pager_pagecount(pBt->pPager) ){
+ if( pgno>sqlite3PagerPagecount(pBt->pPager) ){
return SQLITE_CORRUPT_BKPT;
}
rc = getAndInitPage(pBt, pgno, &pPage, pParent);
if( rc ) goto cleardatabasepage_out;
- rc = sqlite3pager_write(pPage->aData);
- if( rc ) goto cleardatabasepage_out;
for(i=0; i<pPage->nCell; i++){
pCell = findCell(pPage, i);
if( !pPage->leaf ){
@@ -5611,7 +5640,7 @@ static int clearDatabasePage(
}
if( freePageFlag ){
rc = freePage(pPage);
- }else{
+ }else if( (rc = sqlite3PagerWrite(pPage->pDbPage))==0 ){
zeroPage(pPage, pPage->aData[0] | PTF_LEAF);
}
@@ -5631,25 +5660,13 @@ cleardatabasepage_out:
*/
int sqlite3BtreeClearTable(Btree *p, int iTable){
int rc;
- BtCursor *pCur;
BtShared *pBt = p->pBt;
- sqlite3 *db = p->pSqlite;
if( p->inTrans!=TRANS_WRITE ){
return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
}
-
- /* If this connection is not in read-uncommitted mode and currently has
- ** a read-cursor open on the table being cleared, return SQLITE_LOCKED.
- */
- if( 0==db || 0==(db->flags&SQLITE_ReadUncommitted) ){
- for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
- if( pCur->pBtree==p && pCur->pgnoRoot==(Pgno)iTable ){
- if( 0==pCur->wrFlag ){
- return SQLITE_LOCKED;
- }
- moveToRoot(pCur);
- }
- }
+ rc = checkReadLocks(p, iTable, 0);
+ if( rc ){
+ return rc;
}
/* Save the position of all cursors open on this table */
@@ -5699,7 +5716,7 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
return SQLITE_LOCKED;
}
- rc = getPage(pBt, (Pgno)iTable, &pPage);
+ rc = getPage(pBt, (Pgno)iTable, &pPage, 0);
if( rc ) return rc;
rc = sqlite3BtreeClearTable(p, iTable);
if( rc ){
@@ -5738,7 +5755,7 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
*/
MemPage *pMove;
releasePage(pPage);
- rc = getPage(pBt, maxRootPgno, &pMove);
+ rc = getPage(pBt, maxRootPgno, &pMove, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -5747,7 +5764,7 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
if( rc!=SQLITE_OK ){
return rc;
}
- rc = getPage(pBt, maxRootPgno, &pMove);
+ rc = getPage(pBt, maxRootPgno, &pMove, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -5799,6 +5816,7 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
** free pages is not visible. So Cookie[0] is the same as Meta[1].
*/
int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
+ DbPage *pDbPage;
int rc;
unsigned char *pP1;
BtShared *pBt = p->pBt;
@@ -5814,10 +5832,11 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
}
assert( idx>=0 && idx<=15 );
- rc = sqlite3pager_get(pBt->pPager, 1, (void**)&pP1);
+ rc = sqlite3PagerGet(pBt->pPager, 1, &pDbPage);
if( rc ) return rc;
+ pP1 = (unsigned char *)sqlite3PagerGetData(pDbPage);
*pMeta = get4byte(&pP1[36 + idx*4]);
- sqlite3pager_unref(pP1);
+ sqlite3PagerUnref(pDbPage);
/* If autovacuumed is disabled in this build but we are trying to
** access an autovacuumed database, then make the database readonly.
@@ -5845,7 +5864,7 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
}
assert( pBt->pPage1!=0 );
pP1 = pBt->pPage1->aData;
- rc = sqlite3pager_write(pP1);
+ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
if( rc ) return rc;
put4byte(&pP1[36 + idx*4], iMeta);
return SQLITE_OK;
@@ -5881,7 +5900,7 @@ static int btreePageDump(BtShared *pBt, int pgno, int recursive, MemPage *pParen
char range[20];
unsigned char payload[20];
- rc = getPage(pBt, (Pgno)pgno, &pPage);
+ rc = getPage(pBt, (Pgno)pgno, &pPage, 0);
isInit = pPage->isInit;
if( pPage->isInit==0 ){
initPage(pPage, pParent);
@@ -5960,7 +5979,7 @@ static int btreePageDump(BtShared *pBt, int pgno, int recursive, MemPage *pParen
btreePageDump(pBt, get4byte(&data[hdr+8]), 1, pPage);
}
pPage->isInit = isInit;
- sqlite3pager_unref(data);
+ sqlite3PagerUnref(pPage->pDbPage);
fflush(stdout);
return SQLITE_OK;
}
@@ -5969,7 +5988,7 @@ int sqlite3BtreePageDump(Btree *p, int pgno, int recursive){
}
#endif
-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
+#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
/*
** Fill aResult[] with information about the entry and page that the
** cursor is pointing to.
@@ -5984,6 +6003,7 @@ int sqlite3BtreePageDump(Btree *p, int pgno, int recursive){
** aResult[7] = Header size in bytes
** aResult[8] = Local payload size
** aResult[9] = Parent page number
+** aResult[10]= Page number of the first overflow page
**
** This routine is used for testing and debugging only.
*/
@@ -5992,20 +6012,18 @@ int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
MemPage *pPage = pCur->pPage;
BtCursor tmpCur;
- int rc = restoreOrClearCursorPosition(pCur, 1);
+ int rc = restoreOrClearCursorPosition(pCur);
if( rc!=SQLITE_OK ){
return rc;
}
- pageIntegrity(pPage);
assert( pPage->isInit );
getTempCursor(pCur, &tmpCur);
while( upCnt-- ){
moveToParent(&tmpCur);
}
pPage = tmpCur.pPage;
- pageIntegrity(pPage);
- aResult[0] = sqlite3pager_pagenumber(pPage->aData);
+ aResult[0] = sqlite3PagerPagenumber(pPage->pDbPage);
assert( aResult[0]==pPage->pgno );
aResult[1] = tmpCur.idx;
aResult[2] = pPage->nCell;
@@ -6034,6 +6052,11 @@ int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
}else{
aResult[9] = pPage->pParent->pgno;
}
+ if( tmpCur.info.iOverflow ){
+ aResult[10] = get4byte(&tmpCur.info.pCell[tmpCur.info.iOverflow]);
+ }else{
+ aResult[10] = 0;
+ }
releaseTempCursor(&tmpCur);
return SQLITE_OK;
}
@@ -6054,10 +6077,12 @@ Pager *sqlite3BtreePager(Btree *p){
typedef struct IntegrityCk IntegrityCk;
struct IntegrityCk {
BtShared *pBt; /* The tree being checked out */
- Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
- int nPage; /* Number of pages in the database */
- int *anRef; /* Number of times each page is referenced */
- char *zErrMsg; /* An error message. NULL of no errors seen. */
+ Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
+ int nPage; /* Number of pages in the database */
+ int *anRef; /* Number of times each page is referenced */
+ int mxErr; /* Stop accumulating errors when this reaches zero */
+ char *zErrMsg; /* An error message. NULL if no errors seen. */
+ int nErr; /* Number of messages written to zErrMsg so far */
};
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
@@ -6072,6 +6097,9 @@ static void checkAppendMsg(
){
va_list ap;
char *zMsg2;
+ if( !pCheck->mxErr ) return;
+ pCheck->mxErr--;
+ pCheck->nErr++;
va_start(ap, zFormat);
zMsg2 = sqlite3VMPrintf(zFormat, ap);
va_end(ap);
@@ -6155,8 +6183,9 @@ static void checkList(
int i;
int expected = N;
int iFirst = iPage;
- while( N-- > 0 ){
- unsigned char *pOvfl;
+ while( N-- > 0 && pCheck->mxErr ){
+ DbPage *pOvflPage;
+ unsigned char *pOvflData;
if( iPage<1 ){
checkAppendMsg(pCheck, zContext,
"%d of %d pages missing from overflow list starting at %d",
@@ -6164,12 +6193,13 @@ static void checkList(
break;
}
if( checkRef(pCheck, iPage, zContext) ) break;
- if( sqlite3pager_get(pCheck->pPager, (Pgno)iPage, (void**)&pOvfl) ){
+ if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage) ){
checkAppendMsg(pCheck, zContext, "failed to get page %d", iPage);
break;
}
+ pOvflData = (unsigned char *)sqlite3PagerGetData(pOvflPage);
if( isFreeList ){
- int n = get4byte(&pOvfl[4]);
+ int n = get4byte(&pOvflData[4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext);
@@ -6181,7 +6211,7 @@ static void checkList(
N--;
}else{
for(i=0; i<n; i++){
- Pgno iFreePage = get4byte(&pOvfl[8+i*4]);
+ Pgno iFreePage = get4byte(&pOvflData[8+i*4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0, zContext);
@@ -6199,13 +6229,13 @@ static void checkList(
** the following page matches iPage.
*/
if( pCheck->pBt->autoVacuum && N>0 ){
- i = get4byte(pOvfl);
+ i = get4byte(pOvflData);
checkPtrmap(pCheck, i, PTRMAP_OVERFLOW2, iPage, zContext);
}
}
#endif
- iPage = get4byte(pOvfl);
- sqlite3pager_unref(pOvfl);
+ iPage = get4byte(pOvflData);
+ sqlite3PagerUnref(pOvflPage);
}
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -6253,7 +6283,7 @@ static int checkTreePage(
usableSize = pBt->usableSize;
if( iPage==0 ) return 0;
if( checkRef(pCheck, iPage, zParentContext) ) return 0;
- if( (rc = getPage(pBt, (Pgno)iPage, &pPage))!=0 ){
+ if( (rc = getPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
checkAppendMsg(pCheck, zContext,
"unable to get the page. error code=%d", rc);
return 0;
@@ -6267,7 +6297,7 @@ static int checkTreePage(
/* Check out all the cells.
*/
depth = 0;
- for(i=0; i<pPage->nCell; i++){
+ for(i=0; i<pPage->nCell && pCheck->mxErr; i++){
u8 *pCell;
int sz;
CellInfo info;
@@ -6279,6 +6309,7 @@ static int checkTreePage(
parseCellPtr(pPage, pCell, &info);
sz = info.nData;
if( !pPage->intKey ) sz += info.nKey;
+ assert( sz==info.nPayload );
if( sz>info.nLocal ){
int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4);
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
@@ -6382,19 +6413,28 @@ static int checkTreePage(
** and a pointer to that error message is returned. The calling function
** is responsible for freeing the error message when it is done.
*/
-char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
+char *sqlite3BtreeIntegrityCheck(
+ Btree *p, /* The btree to be checked */
+ int *aRoot, /* An array of root pages numbers for individual trees */
+ int nRoot, /* Number of entries in aRoot[] */
+ int mxErr, /* Stop reporting errors after this many */
+ int *pnErr /* Write number of errors seen to this variable */
+){
int i;
int nRef;
IntegrityCk sCheck;
BtShared *pBt = p->pBt;
- nRef = sqlite3pager_refcount(pBt->pPager);
+ nRef = sqlite3PagerRefcount(pBt->pPager);
if( lockBtreeWithRetry(p)!=SQLITE_OK ){
return sqliteStrDup("Unable to acquire a read lock on the database");
}
sCheck.pBt = pBt;
sCheck.pPager = pBt->pPager;
- sCheck.nPage = sqlite3pager_pagecount(sCheck.pPager);
+ sCheck.nPage = sqlite3PagerPagecount(sCheck.pPager);
+ sCheck.mxErr = mxErr;
+ sCheck.nErr = 0;
+ *pnErr = 0;
if( sCheck.nPage==0 ){
unlockBtreeIfUnused(pBt);
return 0;
@@ -6402,6 +6442,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
sCheck.anRef = sqliteMallocRaw( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) );
if( !sCheck.anRef ){
unlockBtreeIfUnused(pBt);
+ *pnErr = 1;
return sqlite3MPrintf("Unable to malloc %d bytes",
(sCheck.nPage+1)*sizeof(sCheck.anRef[0]));
}
@@ -6419,7 +6460,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
/* Check all the tables.
*/
- for(i=0; i<nRoot; i++){
+ for(i=0; i<nRoot && sCheck.mxErr; i++){
if( aRoot[i]==0 ) continue;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum && aRoot[i]>1 ){
@@ -6431,7 +6472,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
/* Make sure every page in the file is referenced
*/
- for(i=1; i<=sCheck.nPage; i++){
+ for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM
if( sCheck.anRef[i]==0 ){
checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
@@ -6454,16 +6495,17 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
/* Make sure this analysis did not leave any unref() pages
*/
unlockBtreeIfUnused(pBt);
- if( nRef != sqlite3pager_refcount(pBt->pPager) ){
+ if( nRef != sqlite3PagerRefcount(pBt->pPager) ){
checkAppendMsg(&sCheck, 0,
"Outstanding page count goes from %d to %d during this analysis",
- nRef, sqlite3pager_refcount(pBt->pPager)
+ nRef, sqlite3PagerRefcount(pBt->pPager)
);
}
/* Clean up and report errors.
*/
sqliteFree(sCheck.anRef);
+ *pnErr = sCheck.nErr;
return sCheck.zErrMsg;
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -6473,7 +6515,7 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){
*/
const char *sqlite3BtreeGetFilename(Btree *p){
assert( p->pBt->pPager!=0 );
- return sqlite3pager_filename(p->pBt->pPager);
+ return sqlite3PagerFilename(p->pBt->pPager);
}
/*
@@ -6481,7 +6523,7 @@ const char *sqlite3BtreeGetFilename(Btree *p){
*/
const char *sqlite3BtreeGetDirname(Btree *p){
assert( p->pBt->pPager!=0 );
- return sqlite3pager_dirname(p->pBt->pPager);
+ return sqlite3PagerDirname(p->pBt->pPager);
}
/*
@@ -6491,7 +6533,7 @@ const char *sqlite3BtreeGetDirname(Btree *p){
*/
const char *sqlite3BtreeGetJournalname(Btree *p){
assert( p->pBt->pPager!=0 );
- return sqlite3pager_journalname(p->pBt->pPager);
+ return sqlite3PagerJournalname(p->pBt->pPager);
}
#ifndef SQLITE_OMIT_VACUUM
@@ -6513,30 +6555,42 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
return SQLITE_ERROR;
}
if( pBtTo->pCursor ) return SQLITE_BUSY;
- nToPage = sqlite3pager_pagecount(pBtTo->pPager);
- nPage = sqlite3pager_pagecount(pBtFrom->pPager);
+ nToPage = sqlite3PagerPagecount(pBtTo->pPager);
+ nPage = sqlite3PagerPagecount(pBtFrom->pPager);
iSkip = PENDING_BYTE_PAGE(pBtTo);
for(i=1; rc==SQLITE_OK && i<=nPage; i++){
- void *pPage;
+ DbPage *pDbPage;
if( i==iSkip ) continue;
- rc = sqlite3pager_get(pBtFrom->pPager, i, &pPage);
- if( rc ) break;
- rc = sqlite3pager_overwrite(pBtTo->pPager, i, pPage);
+ rc = sqlite3PagerGet(pBtFrom->pPager, i, &pDbPage);
if( rc ) break;
- sqlite3pager_unref(pPage);
+ rc = sqlite3PagerOverwrite(pBtTo->pPager, i, sqlite3PagerGetData(pDbPage));
+ sqlite3PagerUnref(pDbPage);
}
+
+ /* If the file is shrinking, journal the pages that are being truncated
+ ** so that they can be rolled back if the commit fails.
+ */
for(i=nPage+1; rc==SQLITE_OK && i<=nToPage; i++){
- void *pPage;
+ DbPage *pDbPage;
if( i==iSkip ) continue;
- rc = sqlite3pager_get(pBtTo->pPager, i, &pPage);
+ rc = sqlite3PagerGet(pBtTo->pPager, i, &pDbPage);
if( rc ) break;
- rc = sqlite3pager_write(pPage);
- sqlite3pager_unref(pPage);
- sqlite3pager_dont_write(pBtTo->pPager, i);
+ rc = sqlite3PagerWrite(pDbPage);
+ sqlite3PagerDontWrite(pDbPage);
+ /* Yeah. It seems wierd to call DontWrite() right after Write(). But
+ ** that is because the names of those procedures do not exactly
+ ** represent what they do. Write() really means "put this page in the
+ ** rollback journal and mark it as dirty so that it will be written
+ ** to the database file later." DontWrite() undoes the second part of
+ ** that and prevents the page from being written to the database. The
+ ** page is still on the rollback journal, though. And that is the whole
+ ** point of this loop: to put pages on the rollback journal. */
+ sqlite3PagerUnref(pDbPage);
}
if( !rc && nPage<nToPage ){
- rc = sqlite3pager_truncate(pBtTo->pPager, nPage);
+ rc = sqlite3PagerTruncate(pBtTo->pPager, nPage);
}
+
if( rc ){
sqlite3BtreeRollback(pTo);
}
@@ -6566,38 +6620,6 @@ int sqlite3BtreeIsInReadTrans(Btree *p){
}
/*
-** This call is a no-op if no write-transaction is currently active on pBt.
-**
-** Otherwise, sync the database file for the btree pBt. zMaster points to
-** the name of a master journal file that should be written into the
-** individual journal file, or is NULL, indicating no master journal file
-** (single database transaction).
-**
-** When this is called, the master journal should already have been
-** created, populated with this journal pointer and synced to disk.
-**
-** Once this is routine has returned, the only thing required to commit
-** the write-transaction for this database file is to delete the journal.
-*/
-int sqlite3BtreeSync(Btree *p, const char *zMaster){
- int rc = SQLITE_OK;
- if( p->inTrans==TRANS_WRITE ){
- BtShared *pBt = p->pBt;
- Pgno nTrunc = 0;
-#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum ){
- rc = autoVacuumCommit(pBt, &nTrunc);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- }
-#endif
- rc = sqlite3pager_sync(pBt->pPager, zMaster, nTrunc);
- }
- return rc;
-}
-
-/*
** This function returns a pointer to a blob of memory associated with
** a single shared-btree. The memory is used by client code for it's own
** purposes (for example, to store a high-level schema associated with
@@ -6667,7 +6689,7 @@ int sqlite3_shared_cache_report(
BtShared *pBt;
Tcl_Obj *pRet = Tcl_NewObj();
for(pBt=pTd->pBtree; pBt; pBt=pBt->pNext){
- const char *zFile = sqlite3pager_filename(pBt->pPager);
+ const char *zFile = sqlite3PagerFilename(pBt->pPager);
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(zFile, -1));
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(pBt->nRef));
}