diff --git a/manifest b/manifest index 8e99792825..ddb29e4fe1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Got\sa\slot\sof\sBTree\stests\sworking.\sStill\slots\smore\sneeded.\s(CVS\s230) -D 2001-06-28T01:54:48 +C More\sBTree\stests\sand\sa\sfew\sbug\sfixes.\s(CVS\s231) +D 2001-06-28T11:50:22 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4 F Makefile.in 63bc9a6a39b7160ce8d2392ae74eb4ca4ca84c6e F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 @@ -12,7 +12,7 @@ F notes/notes1.txt b7c0812b704a022e88c621146ae50955c923d464 F notes/notes2.txt 7e3fafd5e25906c1fe1e95f13b089aa398ca403e F notes/notes3.txt 985bf688b59f1f52bfe6e4b1f896efdeffac1432 F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4 -F src/btree.c d55ba210df7625c1edd62a4631bb6d322d9b68ca +F src/btree.c 19c77416536851667983be43af62c631fb395a92 F src/btree.h d327e9ad671d41d41aa2dd376c9230c8d2167c8e F src/build.c 4f6a2d551c56342cd4a0420654835be3ad179651 F src/dbbe.c b18259f99d87240cbe751021cf14dd3aa83a48af @@ -53,7 +53,7 @@ F src/vdbe.c f93be4414ba892df9c5589815d2a57c1fb12c820 F src/vdbe.h dc1205da434c6a9da03b5d6b089270bbc8e6d437 F src/where.c 0c542fc44bd85152dfb8507862cfe2e60c629e9f F test/all.test 21d55a97e39e7ec5776751dc9dd8b1b51ef4a048 -F test/btree.test dc07031aaa753fb230b0d30166b5f00e467afa49 +F test/btree.test 6299ba795987b28fddd62e0869211c97ba311bcc F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb F test/dbbe.test a022fe2d983848f786e17ef1fc6809cfd37fb02c F test/delete.test 50b9b1f06c843d591741dba7869433a105360dbf @@ -108,7 +108,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2 F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad -P 6b9b298b2846146b95d7df7f423867976bafa390 -R 352f1f1690fcb389985c90a80a4e8ed3 +P 9cfeeb5896d2a17c8c7904136d346a6245c9e497 +R 143955071809794a38422b6ba77e94d9 U drh -Z 2f00c60c42da64c07beeccb2fde6cfaa +Z c80cdc0e1cd9e727a881d0fcbcd59b2a diff --git a/manifest.uuid b/manifest.uuid index e0131c98ab..9c251141e7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9cfeeb5896d2a17c8c7904136d346a6245c9e497 \ No newline at end of file +2c9127943cd5a541613924d2df773c4e8df4c1a6 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 7dbac878a6..f701535476 100644 --- a/src/btree.c +++ b/src/btree.c @@ -21,7 +21,7 @@ ** http://www.hwaci.com/drh/ ** ************************************************************************* -** $Id: btree.c,v 1.16 2001/06/28 01:54:48 drh Exp $ +** $Id: btree.c,v 1.17 2001/06/28 11:50:22 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -1623,6 +1623,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){ int usedPerPage; /* Memory needed for each page */ int freePerPage; /* Average free space per page */ int totalSize; /* Total bytes for all cells */ + MemPage *extraUnref = 0; /* A page that needs to be unref-ed */ Pgno pgno; /* Page number */ Cell *apCell[MX_CELL*3+5]; /* All cells from pages being balanceed */ int szCell[MX_CELL*3+5]; /* Local size of all cells */ @@ -1693,6 +1694,8 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){ if( pCur ){ sqlitepager_unref(pCur->pPage); pCur->pPage = pChild; + }else{ + extraUnref = pChild; } zeroPage(pPage); pPage->u.hdr.rightChild = pgnoChild; @@ -1767,7 +1770,7 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){ */ if( pCur ){ iCur = pCur->idx; - for(i=0; idxDiv[i]nCell + 1; } sqlitepager_unref(pCur->pPage); @@ -1888,6 +1891,9 @@ static int balance(Btree *pBt, MemPage *pPage, BtCursor *pCur){ ** Cleanup before returning. */ balance_cleanup: + if( extraUnref ){ + sqlitepager_unref(extraUnref); + } for(i=0; iapCell[pCur->idx]; pgnoChild = pCell->h.leftChild; clearCell(pCur->pBt, pCell); - dropCell(pPage, pCur->idx, cellSize(pCell)); if( pgnoChild ){ /* - ** If the entry we just deleted is not a leaf, then we've left a - ** hole in an internal page. We have to fill the hole by moving - ** in a cell from a leaf. The next Cell after the one just deleted - ** is guaranteed to exist and to be a leaf so we can use it. + ** If the entry we are about to delete is not a leaf so if we do not + ** do something we will leave a hole on an internal page. + ** We have to fill the hole by moving in a cell from a leaf. The + ** next Cell after the one to be deleted is guaranteed to exist and + ** to be a leaf so we can use it. */ BtCursor leafCur; Cell *pNext; @@ -1991,6 +1997,7 @@ int sqliteBtreeDelete(BtCursor *pCur){ if( rc!=SQLITE_OK ){ return SQLITE_CORRUPT; } + dropCell(pPage, pCur->idx, cellSize(pCell)); pNext = leafCur.pPage->apCell[leafCur.idx]; szNext = cellSize(pNext); pNext->h.leftChild = pgnoChild; @@ -2002,6 +2009,7 @@ int sqliteBtreeDelete(BtCursor *pCur){ rc = balance(pCur->pBt, leafCur.pPage, 0); releaseTempCursor(&leafCur); }else{ + dropCell(pPage, pCur->idx, cellSize(pCell)); rc = balance(pCur->pBt, pPage, pCur); pCur->bSkipNext = 1; } diff --git a/test/btree.test b/test/btree.test index 545671b50b..b73a29bfc5 100644 --- a/test/btree.test +++ b/test/btree.test @@ -23,7 +23,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is btree database backend # -# $Id: btree.test,v 1.3 2001/06/28 01:54:50 drh Exp $ +# $Id: btree.test,v 1.4 2001/06/28 11:50:22 drh Exp $ set testdir [file dirname $argv0] @@ -791,6 +791,224 @@ do_test btree-9.7 { lindex [btree_pager_stats $::b1] 1 } {0} +# Create a tree of depth two. That is, there is a single divider entry +# on the root pages and two leaf pages. Then delete the divider entry +# see what happens. +# +do_test btree-10.1 { + btree_begin_transaction $::b1 + btree_drop_table $::b1 2 + lindex [btree_pager_stats $::b1] 1 +} {1} +do_test btree-10.2 { + set ::c1 [btree_cursor $::b1 2] + lindex [btree_pager_stats $::b1] 1 +} {2} +do_test btree-10.3 { + for {set i 1} {$i<=20} {incr i} { + set key [format %03d $i] + set data "*** $key *** $key *** $key *** $key ***" + btree_insert $::c1 $key $data + } + select_keys $::c1 +} {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020} +#btree_page_dump $::b1 7 +#btree_page_dump $::b1 2 +#btree_page_dump $::b1 6 +do_test btree-10.4 { + btree_move_to $::c1 011 + btree_delete $::c1 + select_keys $::c1 +} {001 002 003 004 005 006 007 008 009 010 012 013 014 015 016 017 018 019 020} +#btree_page_dump $::b1 2 +for {set i 1} {$i<=20} {incr i} { + do_test btree-10.5.$i { + btree_move_to $::c1 [format %03d $i] + lindex [btree_pager_stats $::b1] 1 + } {2} +} + +# Create a tree with lots more pages +# +catch {unset ::data} +catch {unset ::key} +for {set i 21} {$i<=1000} {incr i} { + do_test btree-11.1.$i.1 { + set key [format %03d $i] + set ::data "*** $key *** $key *** $key *** $key ***" + btree_insert $::c1 $key $data + btree_key $::c1 + } [format %03d $i] + do_test btree-11.1.$i.2 { + btree_data $::c1 + } $::data + set ::key [format %03d [expr {$i/2}]] + if {$::key=="011"} {set ::key 010} + do_test btree-11.1.$i.3 { + btree_move_to $::c1 $::key + btree_key $::c1 + } $::key +} +catch {unset ::data} +catch {unset ::key} + +# Make sure our reference count is still correct. +# +do_test btree-11.2 { + btree_close_cursor $::c1 + lindex [btree_pager_stats $::b1] 1 +} {1} +do_test btree-11.3 { + set ::c1 [btree_cursor $::b1 2] + lindex [btree_pager_stats $::b1] 1 +} {2} +#btree_page_dump $::b1 2 + +# Delete the dividers on the root page +# +do_test btree-11.4 { + btree_move_to $::c1 257 + btree_delete $::c1 + btree_next $::c1 + btree_key $::c1 +} {258} +do_test btree-11.4.1 { + btree_move_to $::c1 256 + btree_key $::c1 +} {256} +do_test btree-11.4.2 { + btree_move_to $::c1 258 + btree_key $::c1 +} {258} +do_test btree-11.4.3 { + btree_move_to $::c1 259 + btree_key $::c1 +} {259} +do_test btree-11.4.4 { + btree_move_to $::c1 257 + btree_key $::c1 +} {256} +do_test btree-11.5 { + btree_move_to $::c1 513 + btree_delete $::c1 + btree_next $::c1 + btree_key $::c1 +} {514} +do_test btree-11.5.1 { + btree_move_to $::c1 512 + btree_key $::c1 +} {512} +do_test btree-11.5.2 { + btree_move_to $::c1 514 + btree_key $::c1 +} {514} +do_test btree-11.5.3 { + btree_move_to $::c1 515 + btree_key $::c1 +} {515} +do_test btree-11.5.4 { + btree_move_to $::c1 513 + btree_key $::c1 +} {512} +do_test btree-11.6 { + btree_move_to $::c1 769 + btree_delete $::c1 + btree_next $::c1 + btree_key $::c1 +} {770} +do_test btree-11.6.1 { + btree_move_to $::c1 768 + btree_key $::c1 +} {768} +do_test btree-11.6.2 { + btree_move_to $::c1 771 + btree_key $::c1 +} {771} +do_test btree-11.6.3 { + btree_move_to $::c1 770 + btree_key $::c1 +} {770} +do_test btree-11.6.4 { + btree_move_to $::c1 769 + btree_key $::c1 +} {768} +#btree_page_dump $::b1 2 +#btree_page_dump $::b1 25 + +# Change the data on an intermediate node such that the node becomes overfull +# and has to split. We happen to know that intermediate nodes exist on +# 337, 401 and 465 by the btree_page_dumps above +# +catch {unset ::data} +set ::data {This is going to be a very long data segment} +append ::data $::data +append ::data $::data +do_test btree-12.1 { + btree_insert $::c1 337 $::data + btree_data $::c1 +} $::data +do_test btree-12.2 { + btree_insert $::c1 401 $::data + btree_data $::c1 +} $::data +do_test btree-12.3 { + btree_insert $::c1 465 $::data + btree_data $::c1 +} $::data +do_test btree-12.4 { + btree_move_to $::c1 337 + btree_key $::c1 +} {337} +do_test btree-12.5 { + btree_data $::c1 +} $::data +do_test btree-12.6 { + btree_next $::c1 + btree_key $::c1 +} {338} +do_test btree-12.7 { + btree_move_to $::c1 464 + btree_key $::c1 +} {464} +do_test btree-12.8 { + btree_next $::c1 + btree_data $::c1 +} $::data +do_test btree-12.9 { + btree_next $::c1 + btree_key $::c1 +} {466} +do_test btree-12.10 { + btree_move_to $::c1 400 + btree_key $::c1 +} {400} +do_test btree-12.11 { + btree_next $::c1 + btree_data $::c1 +} $::data +do_test btree-12.12 { + btree_next $::c1 + btree_key $::c1 +} {402} + +# To Do: +# +# 1. Do some deletes from the 3-layer tree +# 2. Commit and reopen the database +# 3. Read every 15th entry and make sure it works +# 4. Implement btree_sanity and put it throughout this script +# + +do_test btree-10.98 { + btree_close_cursor $::c1 + lindex [btree_pager_stats $::b1] 1 +} {1} +do_test btree-10.99 { + btree_rollback $::b1 + lindex [btree_pager_stats $::b1] 1 +} {0} +btree_pager_ref_dump $::b1 + do_test btree-99.1 { btree_close $::b1 } {}