1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

Add the shared schema/pager modifications. Very few tests so far. (CVS 2859)

FossilOrigin-Name: deeda0dc06c1595aedd8d06a0c4e88a8abf78cf7
This commit is contained in:
danielk1977
2006-01-05 11:34:32 +00:00
parent a6370df1e1
commit da18423620
27 changed files with 1077 additions and 375 deletions

View File

@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.278 2005/12/30 16:31:54 danielk1977 Exp $
** $Id: btree.c,v 1.279 2006/01/05 11:34:34 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@@ -349,6 +349,8 @@ struct BtShared {
int nRef; /* Number of references to this structure */
int nTransaction; /* Number of open transactions (read + write) */
BtLock *pLock; /* List of locks held on this shared-btree struct */
void *pSchema;
void (*xFreeSchema)(void*);
};
/*
@@ -382,9 +384,38 @@ struct BtCursor {
int idx; /* Index of the entry in pPage->aCell[] */
CellInfo info; /* A parse of the cell we are pointing at */
u8 wrFlag; /* True if writable */
u8 isValid; /* TRUE if points to a valid entry */
u8 eState; /* One of the CURSOR_XXX constants (see below) */
#ifndef SQLITE_OMIT_SHARED_CACHE
void *pKey;
i64 nKey;
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.
**
** CURSOR_VALID:
** Cursor points to a valid entry. getPayload() etc. may be called.
**
** CURSOR_INVALID:
** Cursor does not point to a valid entry. This can happen (for example)
** because the table is empty or because BtreeCursorFirst() has not been
** called.
**
** CURSOR_REQUIRESEEK:
** The table that this cursor was opened on still exists, but has been
** modified since the cursor was last used. The cursor position is saved
** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in
** this state, restoreCursorPosition() can be called to attempt to seek
** the cursor to the saved position.
*/
#define CURSOR_INVALID 0
#define CURSOR_VALID 1
#define CURSOR_REQUIRESEEK 2
/*
** The TRACE macro will print high-level status information about the
** btree operation when the global variable sqlite3_btree_trace is
@@ -462,14 +493,110 @@ struct BtLock {
** manipulate entries in the BtShared.pLock linked list used to store
** shared-cache table level locks. If the library is compiled with the
** shared-cache feature disabled, then there is only ever one user
** of each BtShared structure and so this locking is not required.
** So define the three interface functions as no-ops.
** of each BtShared structure and so this locking is not necessary.
** So define the lock related functions as no-ops.
*/
#define queryTableLock(a,b,c) SQLITE_OK
#define lockTable(a,b,c) SQLITE_OK
#define unlockAllTables(a,b,c)
#define unlockAllTables(a)
#define restoreCursorPosition(a,b) SQLITE_OK
#define saveAllCursors(a,b,c) SQLITE_OK
#else
/*
** 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 = SQLITE_OK;
assert( CURSOR_VALID==pCur->eState|| CURSOR_INVALID==pCur->eState );
assert( 0==pCur->pKey );
if( pCur->eState==CURSOR_VALID ){
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( pKey ){
pCur->pKey = pKey;
}else{
sqliteFree(pKey);
}
}else{
rc = SQLITE_NOMEM;
}
}
assert( !pCur->pPage->intKey || !pCur->pKey );
/* Todo: Should we drop the reference to pCur->pPage here? */
if( rc==SQLITE_OK ){
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( sqlite3Tsd()->useSharedData ){
for(p=pBt->pCursor; p; p=p->pNext){
if( p!=pExcept && 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 restoreCursorPosition() 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 restoreCursorPosition(BtCursor *pCur, int doSeek){
int rc = SQLITE_OK;
if( pCur->eState==CURSOR_REQUIRESEEK ){
assert( sqlite3Tsd()->useSharedData );
if( doSeek ){
rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip);
}else{
pCur->eState = CURSOR_INVALID;
}
if( rc==SQLITE_OK ){
sqliteFree(pCur->pKey);
pCur->pKey = 0;
assert( CURSOR_VALID==pCur->eState || CURSOR_INVALID==pCur->eState );
}
}
return rc;
}
/*
** Query to see if btree handle p may obtain a lock of type eLock
** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return
@@ -480,10 +607,36 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){
BtShared *pBt = p->pBt;
BtLock *pIter;
for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
if( pIter->pBtree!=p && pIter->iTable==iTab &&
(pIter->eLock!=READ_LOCK || eLock!=READ_LOCK) ){
return SQLITE_BUSY;
/* This is a no-op if the shared-cache is not enabled */
if( 0==sqlite3Tsd()->useSharedData ){
return SQLITE_OK;
}
/* This (along with lockTable()) is where the ReadUncommitted flag is
** dealt with. If the caller is querying for a read-lock and the flag is
** set, it is unconditionally granted - even if there are write-locks
** on the table. If a write-lock is requested, the ReadUncommitted flag
** is not considered.
**
** In function lockTable(), if a read-lock is demanded and the
** ReadUncommitted flag is set, no entry is added to the locks list
** (BtShared.pLock).
**
** To summarize: If the ReadUncommitted flag is set, then read cursors do
** not create or respect table locks. The locking procedure for a
** write-cursor does not change.
*/
if(
!p->pSqlite ||
0==(p->pSqlite->flags&SQLITE_ReadUncommitted) ||
eLock==WRITE_LOCK ||
iTab==1
){
for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
if( pIter->pBtree!=p && pIter->iTable==iTab &&
(pIter->eLock!=eLock || eLock!=READ_LOCK) ){
return SQLITE_BUSY;
}
}
}
return SQLITE_OK;
@@ -502,8 +655,27 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
BtLock *pLock = 0;
BtLock *pIter;
/* This is a no-op if the shared-cache is not enabled */
if( 0==sqlite3Tsd()->useSharedData ){
return SQLITE_OK;
}
assert( SQLITE_OK==queryTableLock(p, iTable, eLock) );
/* If the read-uncommitted flag is set and a read-lock is requested,
** return early without adding an entry to the BtShared.pLock list. See
** comment in function queryTableLock() for more info on handling
** the ReadUncommitted flag.
*/
if(
(p->pSqlite) &&
(p->pSqlite->flags&SQLITE_ReadUncommitted) &&
(eLock==READ_LOCK) &&
iTable!=1
){
return SQLITE_OK;
}
/* First search the list for an existing lock on this table. */
for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
if( pIter->iTable==iTable && pIter->pBtree==p ){
@@ -544,6 +716,13 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
*/
static void unlockAllTables(Btree *p){
BtLock **ppIter = &p->pBt->pLock;
/* If the shared-cache extension is not enabled, there should be no
** locks in the BtShared.pLock list, making this procedure a no-op. Assert
** that this is the case.
*/
assert( sqlite3Tsd()->useSharedData || 0==*ppIter );
while( *ppIter ){
BtLock *pLock = *ppIter;
if( pLock->pBtree==p ){
@@ -680,27 +859,6 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
#endif /* SQLITE_OMIT_AUTOVACUUM */
/*
** Return a pointer to the Btree structure associated with btree pBt
** and connection handle pSqlite.
*/
#if 0
static Btree *btree_findref(BtShared *pBt, sqlite3 *pSqlite){
#ifndef SQLITE_OMIT_SHARED_DATA
if( pBt->aRef ){
int i;
for(i=0; i<pBt->nRef; i++){
if( pBt->aRef[i].pSqlite==pSqlite ){
return &pBt->aRef[i];
}
}
assert(0);
}
#endif
return &pBt->ref;
}
#endif
/*
** Given a btree page and a cell index (0 means the first cell on
** the page, 1 means the second cell, and so forth) return a pointer
@@ -1386,7 +1544,9 @@ int sqlite3BtreeOpen(
int rc;
int nReserve;
unsigned char zDbHeader[100];
#ifndef SQLITE_OMIT_SHARED_CACHE
SqliteTsd *pTsd = sqlite3Tsd();
#endif
/* Set the variable isMemdb to true for an in-memory database, or
** false for a file-based database. This symbol is only required if
@@ -1501,8 +1661,8 @@ int sqlite3BtreeOpen(
pBt->pNext = pTsd->pBtree;
pTsd->pBtree = pBt;
}
pBt->nRef = 1;
#endif
pBt->nRef = 1;
*ppBtree = p;
return SQLITE_OK;
}
@@ -1511,10 +1671,13 @@ int sqlite3BtreeOpen(
** Close an open database and invalidate all cursors.
*/
int sqlite3BtreeClose(Btree *p){
SqliteTsd *pTsd = sqlite3Tsd();
BtShared *pBt = p->pBt;
BtCursor *pCur;
#ifndef SQLITE_OMIT_SHARED_CACHE
SqliteTsd *pTsd = sqlite3Tsd();
#endif
/* Drop any table-locks */
unlockAllTables(p);
@@ -1556,6 +1719,9 @@ int sqlite3BtreeClose(Btree *p){
/* Close the pager and free the shared-btree structure */
assert( !pBt->pCursor );
sqlite3pager_close(pBt->pPager);
if( pBt->xFreeSchema && pBt->pSchema ){
pBt->xFreeSchema(pBt->pSchema);
}
sqliteFree(pBt);
return SQLITE_OK;
}
@@ -2323,7 +2489,7 @@ void sqlite3BtreeCursorList(Btree *p){
sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n",
pCur, pCur->pgnoRoot, zMode,
pPage ? pPage->pgno : 0, pCur->idx,
pCur->isValid ? "" : " eof"
(pCur->eState==CURSOR_VALID) ? "" : " eof"
);
}
}
@@ -2532,7 +2698,7 @@ int sqlite3BtreeCursor(
return rc;
}
}
pCur = sqliteMallocRaw( sizeof(*pCur) );
pCur = sqliteMalloc( sizeof(*pCur) );
if( pCur==0 ){
rc = SQLITE_NOMEM;
goto create_cursor_exception;
@@ -2571,7 +2737,7 @@ int sqlite3BtreeCursor(
}
pCur->pPrev = 0;
pBt->pCursor = pCur;
pCur->isValid = 0;
pCur->eState = CURSOR_INVALID;
*ppCur = pCur;
return SQLITE_OK;
@@ -2604,6 +2770,7 @@ void sqlite3BtreeSetCompare(
*/
int sqlite3BtreeCloseCursor(BtCursor *pCur){
BtShared *pBt = pCur->pBtree->pBt;
restoreCursorPosition(pCur, 0);
if( pCur->pPrev ){
pCur->pPrev->pNext = pCur->pNext;
}else{
@@ -2670,13 +2837,17 @@ static void getCellInfo(BtCursor *pCur){
** itself, not the number of bytes in the key.
*/
int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
if( !pCur->isValid ){
*pSize = 0;
}else{
getCellInfo(pCur);
*pSize = pCur->info.nKey;
int rc = restoreCursorPosition(pCur, 1);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
if( pCur->eState==CURSOR_INVALID ){
*pSize = 0;
}else{
getCellInfo(pCur);
*pSize = pCur->info.nKey;
}
}
return SQLITE_OK;
return rc;
}
/*
@@ -2687,14 +2858,18 @@ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
** the database is empty) then *pSize is set to 0.
*/
int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
if( !pCur->isValid ){
/* Not pointing at a valid entry - set *pSize to 0. */
*pSize = 0;
}else{
getCellInfo(pCur);
*pSize = pCur->info.nData;
int rc = restoreCursorPosition(pCur, 1);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
if( pCur->eState==CURSOR_INVALID ){
/* Not pointing at a valid entry - set *pSize to 0. */
*pSize = 0;
}else{
getCellInfo(pCur);
*pSize = pCur->info.nData;
}
}
return SQLITE_OK;
return rc;
}
/*
@@ -2722,7 +2897,7 @@ static int getPayload(
u32 nKey;
assert( pCur!=0 && pCur->pPage!=0 );
assert( pCur->isValid );
assert( pCur->eState==CURSOR_VALID );
pBt = pCur->pBtree->pBt;
pPage = pCur->pPage;
pageIntegrity(pPage);
@@ -2798,14 +2973,18 @@ static int getPayload(
** the available payload.
*/
int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
assert( pCur->isValid );
assert( pCur->pPage!=0 );
if( pCur->pPage->intKey ){
return SQLITE_CORRUPT_BKPT;
int rc = restoreCursorPosition(pCur, 1);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_VALID );
assert( pCur->pPage!=0 );
if( pCur->pPage->intKey ){
return SQLITE_CORRUPT_BKPT;
}
assert( pCur->pPage->intKey==0 );
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
rc = getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
}
assert( pCur->pPage->intKey==0 );
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
return getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
return rc;
}
/*
@@ -2818,10 +2997,14 @@ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
** the available payload.
*/
int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
assert( pCur->isValid );
assert( pCur->pPage!=0 );
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
return getPayload(pCur, offset, amt, pBuf, 1);
int rc = restoreCursorPosition(pCur, 1);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_VALID );
assert( pCur->pPage!=0 );
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
rc = getPayload(pCur, offset, amt, pBuf, 1);
}
return rc;
}
/*
@@ -2854,7 +3037,7 @@ static const unsigned char *fetchPayload(
int nLocal;
assert( pCur!=0 && pCur->pPage!=0 );
assert( pCur->isValid );
assert( pCur->eState==CURSOR_VALID );
pPage = pCur->pPage;
pageIntegrity(pPage);
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
@@ -2892,10 +3075,16 @@ static const unsigned char *fetchPayload(
** in the common case where no overflow pages are used.
*/
const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){
return (const void*)fetchPayload(pCur, pAmt, 0);
if( pCur->eState==CURSOR_VALID ){
return (const void*)fetchPayload(pCur, pAmt, 0);
}
return 0;
}
const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
return (const void*)fetchPayload(pCur, pAmt, 1);
if( pCur->eState==CURSOR_VALID ){
return (const void*)fetchPayload(pCur, pAmt, 1);
}
return 0;
}
@@ -2909,7 +3098,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
MemPage *pOldPage;
BtShared *pBt = pCur->pBtree->pBt;
assert( pCur->isValid );
assert( pCur->eState==CURSOR_VALID );
rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
if( rc ) return rc;
pageIntegrity(pNewPage);
@@ -2956,7 +3145,7 @@ static void moveToParent(BtCursor *pCur){
MemPage *pPage;
int idxParent;
assert( pCur->isValid );
assert( pCur->eState==CURSOR_VALID );
pPage = pCur->pPage;
assert( pPage!=0 );
assert( !isRootPage(pPage) );
@@ -2981,9 +3170,10 @@ static int moveToRoot(BtCursor *pCur){
int rc;
BtShared *pBt = pCur->pBtree->pBt;
restoreCursorPosition(pCur, 0);
rc = getAndInitPage(pBt, pCur->pgnoRoot, &pRoot, 0);
if( rc ){
pCur->isValid = 0;
pCur->eState = CURSOR_INVALID;
return rc;
}
releasePage(pCur->pPage);
@@ -2996,10 +3186,10 @@ static int moveToRoot(BtCursor *pCur){
assert( pRoot->pgno==1 );
subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]);
assert( subpage>0 );
pCur->isValid = 1;
pCur->eState = CURSOR_VALID;
rc = moveToChild(pCur, subpage);
}
pCur->isValid = pCur->pPage->nCell>0;
pCur->eState = ((pCur->pPage->nCell>0)?CURSOR_VALID:CURSOR_INVALID);
return rc;
}
@@ -3012,7 +3202,7 @@ static int moveToLeftmost(BtCursor *pCur){
int rc;
MemPage *pPage;
assert( pCur->isValid );
assert( pCur->eState==CURSOR_VALID );
while( !(pPage = pCur->pPage)->leaf ){
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
pgno = get4byte(findCell(pPage, pCur->idx));
@@ -3034,7 +3224,7 @@ static int moveToRightmost(BtCursor *pCur){
int rc;
MemPage *pPage;
assert( pCur->isValid );
assert( pCur->eState==CURSOR_VALID );
while( !(pPage = pCur->pPage)->leaf ){
pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
pCur->idx = pPage->nCell;
@@ -3054,7 +3244,7 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
int rc;
rc = moveToRoot(pCur);
if( rc ) return rc;
if( pCur->isValid==0 ){
if( pCur->eState==CURSOR_INVALID ){
assert( pCur->pPage->nCell==0 );
*pRes = 1;
return SQLITE_OK;
@@ -3073,12 +3263,12 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
int rc;
rc = moveToRoot(pCur);
if( rc ) return rc;
if( pCur->isValid==0 ){
if( CURSOR_INVALID==pCur->eState ){
assert( pCur->pPage->nCell==0 );
*pRes = 1;
return SQLITE_OK;
}
assert( pCur->isValid );
assert( pCur->eState==CURSOR_VALID );
*pRes = 0;
rc = moveToRightmost(pCur);
return rc;
@@ -3117,7 +3307,7 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
if( rc ) return rc;
assert( pCur->pPage );
assert( pCur->pPage->isInit );
if( pCur->isValid==0 ){
if( pCur->eState==CURSOR_INVALID ){
*pRes = -1;
assert( pCur->pPage->nCell==0 );
return SQLITE_OK;
@@ -3209,7 +3399,11 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
** the first entry. TRUE is also returned if the table is empty.
*/
int sqlite3BtreeEof(BtCursor *pCur){
return pCur->isValid==0;
/* TODO: What if the cursor is in CURSOR_REQUIRESEEK but all table entries
** have been deleted? This API will need to change to return an error code
** as well as the boolean result value.
*/
return (CURSOR_VALID!=pCur->eState);
}
/*
@@ -3222,8 +3416,21 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
int rc;
MemPage *pPage = pCur->pPage;
#ifndef SQLITE_OMIT_SHARED_CACHE
rc = restoreCursorPosition(pCur, 1);
if( rc!=SQLITE_OK ){
return rc;
}
if( pCur->skip>0 ){
pCur->skip = 0;
*pRes = 0;
return SQLITE_OK;
}
pCur->skip = 0;
#endif
assert( pRes!=0 );
if( pCur->isValid==0 ){
if( CURSOR_INVALID==pCur->eState ){
*pRes = 1;
return SQLITE_OK;
}
@@ -3243,7 +3450,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
do{
if( isRootPage(pPage) ){
*pRes = 1;
pCur->isValid = 0;
pCur->eState = CURSOR_INVALID;
return SQLITE_OK;
}
moveToParent(pCur);
@@ -3275,7 +3482,21 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
int rc;
Pgno pgno;
MemPage *pPage;
if( pCur->isValid==0 ){
#ifndef SQLITE_OMIT_SHARED_CACHE
rc = restoreCursorPosition(pCur, 1);
if( rc!=SQLITE_OK ){
return rc;
}
if( pCur->skip<0 ){
pCur->skip = 0;
*pRes = 0;
return SQLITE_OK;
}
pCur->skip = 0;
#endif
if( CURSOR_INVALID==pCur->eState ){
*pRes = 1;
return SQLITE_OK;
}
@@ -3291,7 +3512,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
}else{
while( pCur->idx==0 ){
if( isRootPage(pPage) ){
pCur->isValid = 0;
pCur->eState = CURSOR_INVALID;
*pRes = 1;
return SQLITE_OK;
}
@@ -4887,7 +5108,9 @@ static int balance(MemPage *pPage, int insert){
static int checkReadLocks(BtShared *pBt, Pgno pgnoRoot, BtCursor *pExclude){
BtCursor *p;
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 ){
moveToRoot(p);
@@ -4929,6 +5152,14 @@ int sqlite3BtreeInsert(
if( checkReadLocks(pBt, 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 */
restoreCursorPosition(pCur, 0);
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
if( rc ){
return rc;
}
rc = sqlite3BtreeMoveto(pCur, pKey, nKey, &loc);
if( rc ) return rc;
pPage = pCur->pPage;
@@ -4946,7 +5177,7 @@ int sqlite3BtreeInsert(
if( rc ) goto end_insert;
assert( szNew==cellSizePtr(pPage, newCell) );
assert( szNew<=MX_CELL_SIZE(pBt) );
if( loc==0 && pCur->isValid ){
if( loc==0 && CURSOR_VALID==pCur->eState ){
int szOld;
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
oldCell = findCell(pPage, pCur->idx);
@@ -5003,8 +5234,19 @@ int sqlite3BtreeDelete(BtCursor *pCur){
if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
}
rc = sqlite3pager_write(pPage->aData);
if( rc ) return rc;
/* 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
** that the entry will be deleted from.
*/
if(
(rc = restoreCursorPosition(pCur, 1)) ||
(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) ||
(rc = sqlite3pager_write(pPage->aData))
){
return rc;
}
/* Locate the cell within it's page and leave pCell pointing to the
** data. The clearCell() call frees any overflow pages associated with the
@@ -5427,6 +5669,16 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
unsigned char *pP1;
BtShared *pBt = p->pBt;
/* Reading a meta-data value requires a read-lock on page 1 (and hence
** the sqlite_master table. We grab this lock regardless of whether or
** not the SQLITE_ReadUncommitted flag is set (the table rooted at page
** 1 is treated as a special case by queryTableLock() and lockTable()).
*/
rc = queryTableLock(p, 1, READ_LOCK);
if( rc!=SQLITE_OK ){
return rc;
}
assert( idx>=0 && idx<=15 );
rc = sqlite3pager_get(pBt->pPager, 1, (void**)&pP1);
if( rc ) return rc;
@@ -5440,7 +5692,9 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
if( idx==4 && *pMeta>0 ) pBt->readOnly = 1;
#endif
return SQLITE_OK;
/* Grab the read-lock on page 1. */
rc = lockTable(p, 1, READ_LOCK);
return rc;
}
/*
@@ -5468,6 +5722,9 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
** is currently pointing to.
*/
int sqlite3BtreeFlags(BtCursor *pCur){
/* TODO: What about CURSOR_REQUIRESEEK state? Probably need to call
** restoreCursorPosition() here.
*/
MemPage *pPage = pCur->pPage;
return pPage ? pPage->aData[pPage->hdrOffset] : 0;
}
@@ -5601,6 +5858,11 @@ int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
MemPage *pPage = pCur->pPage;
BtCursor tmpCur;
int rc = restoreCursorPosition(pCur, 1);
if( rc!=SQLITE_OK ){
return rc;
}
pageIntegrity(pPage);
assert( pPage->isInit );
getTempCursor(pCur, &tmpCur);
@@ -6196,6 +6458,31 @@ int sqlite3BtreeSync(Btree *p, const char *zMaster){
return SQLITE_OK;
}
/*
** 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
** the shared-btree). The btree layer manages reference counting issues.
**
** The first time this is called on a shared-btree, nBytes bytes of memory
** are allocated, zeroed, and returned to the caller. For each subsequent
** call the nBytes parameter is ignored and a pointer to the same blob
** of memory returned.
**
** Just before the shared-btree is closed, the function passed as the
** xFree argument when the memory allocation was made is invoked on the
** blob of allocated memory. This function should not call sqliteFree()
** on the memory, the btree layer does that.
*/
void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
BtShared *pBt = p->pBt;
if( !pBt->pSchema ){
pBt->pSchema = sqliteMalloc(nBytes);
pBt->xFreeSchema = xFree;
}
return pBt->pSchema;
}
#ifndef SQLITE_OMIT_SHARED_CACHE
/*
** Enable the shared pager and schema features.