diff options
author | drh <drh@noemail.net> | 2001-09-23 02:35:53 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2001-09-23 02:35:53 +0000 |
commit | ecdc7530dda7a793c96df4010313792de4a6390c (patch) | |
tree | eaef12ba2ead1e93fd05f62f34ba7cb0f0cf39cc /src/btree.c | |
parent | beae319476c5d81e8f399010dc7dc5a2336f0606 (diff) | |
download | sqlite-ecdc7530dda7a793c96df4010313792de4a6390c.tar.gz sqlite-ecdc7530dda7a793c96df4010313792de4a6390c.zip |
Fixes to the locking and rollback behavior. (CVS 261)
FossilOrigin-Name: 337b3d3b2a903328d9744c111979909a284b8348
Diffstat (limited to 'src/btree.c')
-rw-r--r-- | src/btree.c | 82 |
1 files changed, 67 insertions, 15 deletions
diff --git a/src/btree.c b/src/btree.c index 09742e62c..909ead07d 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.29 2001/09/16 00:13:26 drh Exp $ +** $Id: btree.c,v 1.30 2001/09/23 02:35:53 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -312,6 +312,7 @@ struct Btree { BtCursor *pCursor; /* A list of all open cursors */ PageOne *page1; /* First page of the database */ int inTrans; /* True if a transaction is in progress */ + Hash locks; /* Key: root page number. Data: lock count */ }; typedef Btree Bt; @@ -326,6 +327,7 @@ struct BtCursor { Pgno pgnoRoot; /* The root page of this tree */ MemPage *pPage; /* Page that contains the entry */ int idx; /* Index of the entry in pPage->apCell[] */ + u8 wrFlag; /* True if writable */ u8 bSkipNext; /* sqliteBtreeNext() is no-op if true */ u8 iMatch; /* compare result from last sqliteBtreeMoveto() */ }; @@ -619,6 +621,7 @@ int sqliteBtreeOpen( sqlitepager_set_destructor(pBt->pPager, pageDestructor); pBt->pCursor = 0; pBt->page1 = 0; + sqliteHashInit(&pBt->locks, SQLITE_HASH_INT, 0); *ppBtree = pBt; return SQLITE_OK; } @@ -631,6 +634,7 @@ int sqliteBtreeClose(Btree *pBt){ sqliteBtreeCloseCursor(pBt->pCursor); } sqlitepager_close(pBt->pPager); + sqliteHashClear(&pBt->locks); sqliteFree(pBt); return SQLITE_OK; } @@ -771,17 +775,25 @@ int sqliteBtreeCommit(Btree *pBt){ } /* -** Rollback the transaction in progress. All cursors must be -** closed before this routine is called. +** Rollback the transaction in progress. All cursors will be +** invalided by this operation. Any attempt to use a cursor +** that was open at the beginning of this operation will result +** in an error. ** ** This will release the write lock on the database file. If there ** are no active cursors, it also releases the read lock. */ int sqliteBtreeRollback(Btree *pBt){ int rc; - if( pBt->pCursor!=0 ) return SQLITE_ERROR; + BtCursor *pCur; if( pBt->inTrans==0 ) return SQLITE_OK; pBt->inTrans = 0; + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + if( pCur->pPage ){ + sqlitepager_unref(pCur->pPage); + pCur->pPage = 0; + } + } rc = sqlitepager_rollback(pBt->pPager); unlockBtreeIfUnused(pBt); return rc; @@ -792,9 +804,11 @@ int sqliteBtreeRollback(Btree *pBt){ ** iTable. The act of acquiring a cursor gets a read lock on ** the database file. */ -int sqliteBtreeCursor(Btree *pBt, int iTable, BtCursor **ppCur){ +int sqliteBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){ int rc; BtCursor *pCur; + int nLock; + if( pBt->page1==0 ){ rc = lockBtree(pBt); if( rc!=SQLITE_OK ){ @@ -816,7 +830,15 @@ int sqliteBtreeCursor(Btree *pBt, int iTable, BtCursor **ppCur){ if( rc!=SQLITE_OK ){ goto create_cursor_exception; } + nLock = (int)sqliteHashFind(&pBt->locks, 0, iTable); + if( nLock<0 || (nLock>0 && wrFlag) ){ + rc = SQLITE_LOCKED; + goto create_cursor_exception; + } + nLock = wrFlag ? -1 : nLock+1; + sqliteHashInsert(&pBt->locks, 0, iTable, (void*)nLock); pCur->pBt = pBt; + pCur->wrFlag = wrFlag; pCur->idx = 0; pCur->pNext = pBt->pCursor; if( pCur->pNext ){ @@ -842,6 +864,7 @@ create_cursor_exception: ** when the last cursor is closed. */ int sqliteBtreeCloseCursor(BtCursor *pCur){ + int nLock; Btree *pBt = pCur->pBt; if( pCur->pPrev ){ pCur->pPrev->pNext = pCur->pNext; @@ -851,8 +874,14 @@ int sqliteBtreeCloseCursor(BtCursor *pCur){ if( pCur->pNext ){ pCur->pNext->pPrev = pCur->pPrev; } - sqlitepager_unref(pCur->pPage); + if( pCur->pPage ){ + sqlitepager_unref(pCur->pPage); + } unlockBtreeIfUnused(pBt); + nLock = (int)sqliteHashFind(&pBt->locks, 0, pCur->pgnoRoot); + assert( nLock!=0 ); + nLock = nLock<0 ? 0 : nLock-1; + sqliteHashInsert(&pBt->locks, 0, pCur->pgnoRoot, (void*)nLock); sqliteFree(pCur); return SQLITE_OK; } @@ -865,7 +894,9 @@ static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){ memcpy(pTempCur, pCur, sizeof(*pCur)); pTempCur->pNext = 0; pTempCur->pPrev = 0; - sqlitepager_ref(pTempCur->pPage); + if( pTempCur->pPage ){ + sqlitepager_ref(pTempCur->pPage); + } } /* @@ -873,7 +904,9 @@ static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){ ** function above. */ static void releaseTempCursor(BtCursor *pCur){ - sqlitepager_unref(pCur->pPage); + if( pCur->pPage ){ + sqlitepager_unref(pCur->pPage); + } } /* @@ -888,8 +921,7 @@ int sqliteBtreeKeySize(BtCursor *pCur, int *pSize){ MemPage *pPage; pPage = pCur->pPage; - assert( pPage!=0 ); - if( pCur->idx >= pPage->nCell ){ + if( pPage==0 || pCur->idx >= pPage->nCell ){ *pSize = 0; }else{ pCell = pPage->apCell[pCur->idx]; @@ -971,7 +1003,7 @@ int sqliteBtreeKey(BtCursor *pCur, int offset, int amt, char *zBuf){ if( offset<0 ) return 0; if( amt==0 ) return 0; pPage = pCur->pPage; - assert( pPage!=0 ); + if( pPage==0 ) return 0; if( pCur->idx >= pPage->nCell ){ return 0; } @@ -998,8 +1030,7 @@ int sqliteBtreeDataSize(BtCursor *pCur, int *pSize){ MemPage *pPage; pPage = pCur->pPage; - assert( pPage!=0 ); - if( pCur->idx >= pPage->nCell ){ + if( pPage==0 || pCur->idx >= pPage->nCell ){ *pSize = 0; }else{ pCell = pPage->apCell[pCur->idx]; @@ -1024,8 +1055,7 @@ int sqliteBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf){ if( offset<0 ) return 0; if( amt==0 ) return 0; pPage = pCur->pPage; - assert( pPage!=0 ); - if( pCur->idx >= pPage->nCell ){ + if( pPage==0 || pCur->idx >= pPage->nCell ){ return 0; } pCell = pPage->apCell[pCur->idx]; @@ -1190,6 +1220,7 @@ static int moveToLeftmost(BtCursor *pCur){ */ int sqliteBtreeFirst(BtCursor *pCur, int *pRes){ int rc; + if( pCur->pPage==0 ) return SQLITE_ABORT; rc = moveToRoot(pCur); if( rc ) return rc; if( pCur->pPage->nCell==0 ){ @@ -1225,6 +1256,7 @@ int sqliteBtreeFirst(BtCursor *pCur, int *pRes){ */ int sqliteBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes){ int rc; + if( pCur->pPage==0 ) return SQLITE_ABORT; pCur->bSkipNext = 0; rc = moveToRoot(pCur); if( rc ) return rc; @@ -1275,6 +1307,9 @@ int sqliteBtreeMoveto(BtCursor *pCur, const void *pKey, int nKey, int *pRes){ */ int sqliteBtreeNext(BtCursor *pCur, int *pRes){ int rc; + if( pCur->pPage==0 ){ + return SQLITE_ABORT; + } if( pCur->bSkipNext ){ pCur->bSkipNext = 0; if( pRes ) *pRes = 0; @@ -2045,9 +2080,15 @@ int sqliteBtreeInsert( MemPage *pPage; Btree *pBt = pCur->pBt; + if( pCur->pPage==0 ){ + return SQLITE_ABORT; /* A rollback destroyed this cursor */ + } if( !pCur->pBt->inTrans || nKey+nData==0 ){ return SQLITE_ERROR; /* Must start a transaction first */ } + if( !pCur->wrFlag ){ + return SQLITE_PERM; /* Cursor not open for writing */ + } rc = sqliteBtreeMoveto(pCur, pKey, nKey, &loc); if( rc ) return rc; pPage = pCur->pPage; @@ -2090,12 +2131,18 @@ int sqliteBtreeDelete(BtCursor *pCur){ int rc; Pgno pgnoChild; + if( pCur->pPage==0 ){ + return SQLITE_ABORT; /* A rollback destroyed this cursor */ + } if( !pCur->pBt->inTrans ){ return SQLITE_ERROR; /* Must start a transaction first */ } if( pCur->idx >= pPage->nCell ){ return SQLITE_ERROR; /* The cursor is not pointing to anything */ } + if( !pCur->wrFlag ){ + return SQLITE_PERM; /* Did not open this cursor for writing */ + } rc = sqlitepager_write(pPage); if( rc ) return rc; pCell = pPage->apCell[pCur->idx]; @@ -2207,9 +2254,14 @@ static int clearDatabasePage(Btree *pBt, Pgno pgno, int freePageFlag){ */ int sqliteBtreeClearTable(Btree *pBt, int iTable){ int rc; + int nLock; if( !pBt->inTrans ){ return SQLITE_ERROR; /* Must start a transaction first */ } + nLock = (int)sqliteHashFind(&pBt->locks, 0, iTable); + if( nLock ){ + return SQLITE_LOCKED; + } rc = clearDatabasePage(pBt, (Pgno)iTable, 0); if( rc ){ sqliteBtreeRollback(pBt); |