From ce9a7b1e57d7e492cd07d9ad5935b352033655fc Mon Sep 17 00:00:00 2001 From: danielk1977 Date: Sat, 13 Nov 2004 13:19:56 +0000 Subject: [PATCH] Code toward having btree cursors persist when their table is written too. Doesn't work properly yet. (CVS 2097) FossilOrigin-Name: 5eaa18d43f2996a9d354bb4fb9c81e267e98be29 --- manifest | 17 ++--- manifest.uuid | 2 +- src/btree.c | 181 +++++++++++++++++++++++++++++++++++++++++++---- test/btree8.test | 151 +++++++++++++++++++++++++++++++++++++++ test/quick.test | 3 +- 5 files changed, 331 insertions(+), 23 deletions(-) create mode 100644 test/btree8.test diff --git a/manifest b/manifest index d771c9679f..345360543b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\skeyword\sgenerator\sso\sthat\sit\sworks\swith\sSQLITE_OMIT_ALTERTABLE.\s(CVS\s2096) -D 2004-11-13T03:59:25 +C Code\stoward\shaving\sbtree\scursors\spersist\swhen\stheir\stable\sis\swritten\stoo.\sDoesn't\swork\sproperly\syet.\s(CVS\s2097) +D 2004-11-13T13:19:56 F Makefile.in c4d2416860f472a1e3393714d0372074197565df F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1 @@ -29,7 +29,7 @@ F sqlite3.def dbaeb20c153e1d366e8f421b55a573f5dfc00863 F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689 F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea -F src/btree.c 9fd74df65bad768a441afefc3b73174d45b85d5b +F src/btree.c b0b6238c54302e779aa9cb958c8c6da7cab4a090 F src/btree.h 861e40b759a195ba63819740e484390012cf81ab F src/build.c a95eb1181247368b0ffe2eed121a43735976a964 F src/date.c 4fd4e90b3880dacd67305e96330940dc243ffc10 @@ -101,6 +101,7 @@ F test/btree4.test 3797b4305694c7af6828675b0f4b1424b8ca30e4 F test/btree5.test 8e5ff32c02e685d36516c6499add9375fe1377f2 F test/btree6.test a5ede6bfbbb2ec8b27e62813612c0f28e8f3e027 F test/btree7.test a6d3b842db22af97dd14b989e90a2fd96066b72f +F test/btree8.test eee59cfd3a0151adbc7d85e5373f31ae6d4c3061 F test/capi2.test 1ec97bf8896185aec2366c7d07b01edef6ae4b7e F test/capi3.test 9258ca75fc98d89477015dcd70aa3d2757b142b8 F test/capi3b.test 5b6a66f9f295f79f443b5d3f33187fa5ef6cf336 @@ -161,7 +162,7 @@ F test/pagesize.test 6f94b70ed9645dbe6314b627ae765c5dec8036d9 F test/pragma.test d14ed14407a00c10562ca35128120dc09019ed34 F test/printf.test 92ba4c510b4fc61120ffa4a01820446ed917ae57 F test/progress.test 5ddba78cb6011fba36093973cfb3ac473b8fb96a x -F test/quick.test 9e968949a20b5ed5990c03dc45df3781a03c4b1a +F test/quick.test 91e5b8ae6663dc9e3e754b271f0384f0cae706e6 F test/quote.test 6d75cf635d93ba2484dc9cb378d88cbae9dc2c62 F test/reindex.test 3552c6b944a3fab28cfd3049c04c65cb79419757 F test/rollback.test 4097328d44510277244ef4fa51b22b2f11d7ef4c @@ -257,7 +258,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25 F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0 F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c -P 10c712a21961dbc3bff89c49d5ec3b84b9187c80 -R 6f76dd8333301216fe464e4f92eb6cb4 -U drh -Z da59a89dc62ed0410dba6254f89f1869 +P 60ace9985d4ea2ef0fd245027fab2fffc32014b6 +R 89745b387528d396eaba852b9e2401a3 +U danielk1977 +Z ed00ed3361cf0f8eb48b7fc82890652e diff --git a/manifest.uuid b/manifest.uuid index 720007fefe..6326b69aea 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -60ace9985d4ea2ef0fd245027fab2fffc32014b6 \ No newline at end of file +5eaa18d43f2996a9d354bb4fb9c81e267e98be29 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 2e086b2f90..b132302523 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.216 2004/11/10 11:55:11 danielk1977 Exp $ +** $Id: btree.c,v 1.217 2004/11/13 13:19:56 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -3613,6 +3613,7 @@ static int balance_nonroot(MemPage *pPage){ int *szCell; /* Local size of all cells in apCell[] */ u8 *aCopy[NB]; /* Space for holding data of apCopy[] */ u8 *aSpace; /* Space to hold copies of dividers cells */ + BtCursor *pCur; /* ** Find the parent page. @@ -3861,16 +3862,6 @@ static int balance_nonroot(MemPage *pPage){ zeroPage(pNew, pageFlags); } - /* Free any old pages that were not reused as new pages. - */ - while( i=4 ? pgnoNew[3] : 0, nNew>=4 ? szNew[3] : 0, nNew>=5 ? pgnoNew[4] : 0, nNew>=5 ? szNew[4] : 0)); +#if 0 + /* The following block shows how cells migrated during the balance op. */ + if( sqlite3_btree_trace ){ + char zBuf[200]; + char *zCsr = zBuf; + int a, b, c=0, d=0; + *zCsr = '\0'; + for(a=0; anCell+apCopy[a]->nOverflow; + for(b=0; b<(nOldCells+((a!=nOld-1&&!leafData)?1:0)); b++){ + int x = 0; + Pgno iNewPage; + Pgno iOldPage; + int iNewIndex; + int iOldIndex; + + if( bpgno; + iOldIndex = idxDiv[a]; + } + + while( cntNew[x]<=c ) x++; + if( x>0 && c==cntNew[x-1] && !leafData ){ + iNewPage = pParent->pgno; + iNewIndex = nxDiv + a; + }else{ + assert( x0?cntNew[x-1]:0)-(leafData?0:1); + } + + if( (&zBuf[sizeof(zBuf)])-zCsr > 100 && + (1 || iOldPage!=iNewPage || iOldIndex!=iNewIndex) ){ + zCsr += sprintf(zCsr, " %d.%d->%d.%d", iOldPage, iOldIndex, + iNewPage, iNewIndex); + } + c++; + if( (d==0 && strlen(zBuf)>35) || strlen(zBuf)>60 ){ + TRACE(("%s%s\n", d==0?"BALANCE: Cell migration:":"", zBuf)); + zCsr = zBuf; + d = 1; + } + } + } + assert( c==nCell ); + if( zCsr!=zBuf ){ + TRACE(("%s%s\n", d==0?"BALANCE: Cell migration":"", zBuf)); + } + } +#endif + + /* If there are other cursors that refer to one of the pages involved + ** in the balancing, then adjust these cursors so that they still + ** point to the same cells. + */ + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + int nCellCnt = 0; + int iCell = -1; + Pgno pgno = pCur->pPage->pgno; + + /* If the cursor is not valid, do not do anything with it. */ + if( !pCur->isValid ) continue; + + for(i=0; iCell<0 && ipgno ){ + iCell = nCellCnt + pCur->idx; + break; + } + nCellCnt += (apCopy[i]->nCell + apCopy[i]->nOverflow) + (leafData?0:1); + } + + if( pgno==pParent->pgno ){ + assert( !leafData ); + assert( iCell==-1 ); + if( pCur->idx>=nxDiv && pCur->idx<(nxDiv+nOld-1) ){ + for(i=0; i<=(pCur->idx-nxDiv); i++){ + iCell += (apCopy[i]->nCell + apCopy[i]->nOverflow + 1); + } + } + if( pCur->idx>=(nxDiv+nOld) ){ + TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n", + pCur, pgno, pCur->idx, pgno, pCur->idx+(nNew-nOld))); + pCur->idx += (nNew-nOld); + pCur->info.nSize = 0; + } + } + + if( iCell>=0 ){ + int idxNew; + Pgno pgnoNew; + int x = 0; + + while( cntNew[x]<=iCell ) x++; + if( x>0 && !leafData && cntNew[x-1]==iCell ){ + /* The cell that pCur points to is a divider cell in pParent. */ + pgnoNew = pParent->pgno; + idxNew = nxDiv + x-1; + }else{ + /* The cell that pCur points to is on page apNew[x]. */ + idxNew = iCell-(x>0?cntNew[x-1]:0)-((leafData||x==0)?0:1); + pgnoNew = apNew[x]->pgno; + } + + TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n", + pCur, pgno, pCur->idx, pgnoNew, idxNew)); + + pCur->idx = idxNew; + releasePage(pCur->pPage); + rc = getPage(pBt, pgnoNew, &pCur->pPage); + assert( rc==SQLITE_OK ); + pCur->info.nSize = 0; + } + } + + /* Free any old pages that were not reused as new pages. + */ + for(i=nNew; ipParent==0 ); assert( pPage->nOverflow>0 ); @@ -4136,6 +4253,21 @@ static int balance_deeper(MemPage *pPage){ zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF); put4byte(&pPage->aData[pPage->hdrOffset+8], pgnoChild); TRACE(("BALANCE: copy root %d into %d\n", pPage->pgno, pChild->pgno)); + + /* If there were cursors pointing at this page, point them at the new + ** page instead. Decrement the reference count for the old page and + ** increment it for the new one. + */ + for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ + if( pCur->pPage==pPage ){ + TRACE(("BALANCE: Cursor %p migrates from %d,%d to %d,%d\n", + pCur, pPage->pgno, pCur->idx, pChild->pgno, pCur->idx)); + releasePage(pCur->pPage); + rc = getPage(pBt, pChild->pgno, &pCur->pPage); + assert( rc==SQLITE_OK ); + } + } + rc = balance_nonroot(pChild); releasePage(pChild); return rc; @@ -4183,7 +4315,7 @@ static int checkReadLocks(Btree *pBt, Pgno pgnoRoot, BtCursor *pExclude){ if( p->pgnoRoot!=pgnoRoot || p==pExclude ) continue; if( p->wrFlag==0 ) return SQLITE_LOCKED; if( p->pPage->pgno!=p->pgnoRoot ){ - moveToRoot(p); +/* moveToRoot(p); */ } } return SQLITE_OK; @@ -4210,6 +4342,7 @@ int sqlite3BtreeInsert( Btree *pBt = pCur->pBt; unsigned char *oldCell; unsigned char *newCell = 0; + BtCursor *pCur2; if( pCur->status ){ return pCur->status; /* A rollback destroyed this cursor */ @@ -4261,12 +4394,32 @@ int sqlite3BtreeInsert( assert( pPage->leaf ); } rc = insertCell(pPage, pCur->idx, newCell, szNew, 0); + pCur->isValid = 1; + + /* If there are other cursors pointing at this page with a BtCursor.idx + ** field greater than or equal to 'i', then the cell they refer to + ** has been modified or moved within the page. Fix the cursor. + */ + for(pCur2=pPage->pBt->pCursor; pCur2; pCur2 = pCur2->pNext){ + if( pCur2->pPage==pPage ){ + if( pCur2->idx>=pCur->idx && pCur!=pCur2 && loc!=0 ){ + /* The cell pointed to by pCur2 was shifted one to the right on it's + ** page by this Insert(). + */ + TRACE(("INSERT: Cursor %p migrates from %d,%d to %d,%d\n", + pCur2, pPage->pgno, pCur2->idx, pPage->pgno, pCur2->idx+1)); + pCur2->idx++; + } + pCur2->info.nSize = 0; + } + } + if( rc!=SQLITE_OK ) goto end_insert; rc = balance(pPage); /* sqlite3BtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */ /* fflush(stdout); */ if( rc==SQLITE_OK ){ - moveToRoot(pCur); + /* moveToRoot(pCur); */ } end_insert: sqliteFree(newCell); @@ -4350,6 +4503,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){ rc = insertCell(pPage, pCur->idx, pNext-4, szNext+4, tempCell); if( rc!=SQLITE_OK ) return rc; put4byte(findOverflowCell(pPage, pCur->idx), pgnoChild); + pCur->isValid = 0; rc = balance(pPage); sqliteFree(tempCell); if( rc ) return rc; @@ -4360,6 +4514,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){ TRACE(("DELETE: table=%d delete from leaf %d\n", pCur->pgnoRoot, pPage->pgno)); dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell)); + pCur->isValid = 0; rc = balance(pPage); } moveToRoot(pCur); diff --git a/test/btree8.test b/test/btree8.test new file mode 100644 index 0000000000..33a4049505 --- /dev/null +++ b/test/btree8.test @@ -0,0 +1,151 @@ +# 2004 Jun 4 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this script is btree database backend. +# +# $Id: btree8.test,v 1.1 2004/11/13 13:19:56 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +# Use the SQL interface to create a couple of btree tables, one using +# the flags for an SQL table, the other an SQL index. +# +do_test btree8-1.0 { + execsql { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + CREATE INDEX i1 ON t1(b); + } +} {} +set tnum [execsql {SELECT rootpage FROM sqlite_master where type = 'table'}] +set inum [execsql {SELECT rootpage FROM sqlite_master where type = 'index'}] +db close + +# Open the database at the btree level and begin a transaction +do_test btree8-1.1 { + set ::bt [btree_open test.db 100 0] + btree_begin_transaction $::bt + expr 0 +} {0} + +# For each element in the list $keys, insert an entry into the SQL table +# with the corresponding key value. Check that the cursor used to insert +# the key is left pointing to it after the insert. Then save this cursor +# in the list $csr_list. +# +set keys [list 3178 4886 719 1690 443 4113 1618 310 1320 2028] +set csr_list [list] +set testnum 2 +foreach key $keys { + do_test btree-8-1.$testnum { + set csr [btree_cursor $::bt $::tnum 1] + btree_insert $csr $key [string repeat a 10] + lappend csr_list $csr + btree_key $csr + } $key + incr testnum +} +btree_commit $::bt + +# set btree_trace 1 + +# Now write more entries to the table (and overwriting the ones that exist). +# After each write, check that the cursors created above still point to the +# same entries. +btree_begin_transaction $::bt +set ::write_csr [btree_cursor $::bt $::tnum 1] +for {set i $testnum} {$i < 5000 && $nErr==0 } {incr i} { + set datalen [expr int(rand()*20.0)] + + do_test btree8-1.$i.1 { + btree_insert $::write_csr $i [string repeat x $datalen] + } {} + + set testnum 1 + foreach csr $csr_list key $keys { + incr testnum + do_test btree8-1.$i.$testnum { + btree_key $::csr + } $key + } +} +btree_close_cursor $::write_csr +btree_commit $::bt +if {$::nErr>0} { puts $::csr_list } +foreach csr $csr_list { + btree_close_cursor $csr +} +set csr_list [list] + +# Transform the number $num into a string of length $len by repeating the +# string representation of the number as many times as necessary. Repeats +# are seperated by a '.' character. Eg: +# +# [num_to_string 456 10] -> "456.456.45" +# +proc num_to_string {num len} { + return [string range [string repeat "$num." $len] 0 [expr $len-1]] +} + +foreach key $keys { + lappend skeys [num_to_string $key 20] +} + +# For each element in the list $skeys, insert an entry into the SQL index +# with the corresponding key value. Check that the cursor used to insert +# the key is left pointing to it after the insert. Then save this cursor +# in the list $csr_list. +# +btree_begin_transaction $::bt +set testnum 0 +foreach key $skeys { + incr testnum + do_test btree-8-2.$testnum { + set csr [btree_cursor $::bt $::inum 1] + btree_insert $csr $key "" + lappend csr_list $csr + btree_key $csr + } $key +} +btree_commit $::bt + +# set btree_trace 1 + +# Now write more entries to the index (and overwrite the ones that exist). +# After each write, check that the cursors created above still point to the +# same entries. +btree_begin_transaction $::bt +set ::write_csr [btree_cursor $::bt $::inum 1] +for {set i $testnum} {$i < 5000 && $nErr==0 } {incr i} { + set skey [num_to_string $i 20] + + do_test btree8-2.$i.1 { + btree_insert $::write_csr $skey "" + } {} + + set testnum 1 + foreach csr $csr_list key $skeys { + incr testnum + do_test btree8-2.$i.$testnum { + btree_key $::csr + } $key + } +} +btree_close_cursor $::write_csr +btree_commit $::bt +if {$::nErr>0} { puts $::csr_list } +foreach csr $csr_list { + btree_close_cursor $csr +} +set csr_list [list] + +finish_test + diff --git a/test/quick.test b/test/quick.test index 74c01c0dc4..8cc30cfea0 100644 --- a/test/quick.test +++ b/test/quick.test @@ -10,7 +10,7 @@ #*********************************************************************** # This file runs all tests. # -# $Id: quick.test,v 1.32 2004/11/08 09:26:10 danielk1977 Exp $ +# $Id: quick.test,v 1.33 2004/11/13 13:19:56 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -33,6 +33,7 @@ set EXCLUDE { quick.test utf16.test autovacuum_crash.test + btree8.test } if {[sqlite3 -has-codec]} {