1*4520Snw141292 2*4520Snw141292#pragma ident "%Z%%M% %I% %E% SMI" 3*4520Snw141292 4*4520Snw141292# 2001 September 15 5*4520Snw141292# 6*4520Snw141292# The author disclaims copyright to this source code. In place of 7*4520Snw141292# a legal notice, here is a blessing: 8*4520Snw141292# 9*4520Snw141292# May you do good and not evil. 10*4520Snw141292# May you find forgiveness for yourself and forgive others. 11*4520Snw141292# May you share freely, never taking more than you give. 12*4520Snw141292# 13*4520Snw141292#*********************************************************************** 14*4520Snw141292# This file implements regression tests for SQLite library. The 15*4520Snw141292# focus of this script is btree database backend 16*4520Snw141292# 17*4520Snw141292# $Id: btree2.test,v 1.10 2002/02/19 13:39:23 drh Exp $ 18*4520Snw141292 19*4520Snw141292 20*4520Snw141292set testdir [file dirname $argv0] 21*4520Snw141292source $testdir/tester.tcl 22*4520Snw141292 23*4520Snw141292if {[info commands btree_open]!=""} { 24*4520Snw141292 25*4520Snw141292# Create a new database file containing no entries. The database should 26*4520Snw141292# contain 5 tables: 27*4520Snw141292# 28*4520Snw141292# 2 The descriptor table 29*4520Snw141292# 3 The foreground table 30*4520Snw141292# 4 The background table 31*4520Snw141292# 5 The long key table 32*4520Snw141292# 6 The long data table 33*4520Snw141292# 34*4520Snw141292# An explanation for what all these tables are used for is provided below. 35*4520Snw141292# 36*4520Snw141292do_test btree2-1.1 { 37*4520Snw141292 expr srand(1) 38*4520Snw141292 file delete -force test2.bt 39*4520Snw141292 file delete -force test2.bt-journal 40*4520Snw141292 set ::b [btree_open test2.bt] 41*4520Snw141292 btree_begin_transaction $::b 42*4520Snw141292 btree_create_table $::b 43*4520Snw141292} {3} 44*4520Snw141292do_test btree2-1.2 { 45*4520Snw141292 btree_create_table $::b 46*4520Snw141292} {4} 47*4520Snw141292do_test btree2-1.3 { 48*4520Snw141292 btree_create_table $::b 49*4520Snw141292} {5} 50*4520Snw141292do_test btree2-1.4 { 51*4520Snw141292 btree_create_table $::b 52*4520Snw141292} {6} 53*4520Snw141292do_test btree2-1.5 { 54*4520Snw141292 set ::c2 [btree_cursor $::b 2 1] 55*4520Snw141292 btree_insert $::c2 {one} {1} 56*4520Snw141292 btree_delete $::c2 57*4520Snw141292 btree_close_cursor $::c2 58*4520Snw141292 btree_commit $::b 59*4520Snw141292 btree_integrity_check $::b 2 3 4 5 6 60*4520Snw141292} {} 61*4520Snw141292 62*4520Snw141292# This test module works by making lots of pseudo-random changes to a 63*4520Snw141292# database while simultaneously maintaining an invariant on that database. 64*4520Snw141292# Periodically, the script does a sanity check on the database and verifies 65*4520Snw141292# that the invariant is satisfied. 66*4520Snw141292# 67*4520Snw141292# The invariant is as follows: 68*4520Snw141292# 69*4520Snw141292# 1. The descriptor table always contains 2 enters. An entry keyed by 70*4520Snw141292# "N" is the number of elements in the foreground and background tables 71*4520Snw141292# combined. The entry keyed by "L" is the number of digits in the keys 72*4520Snw141292# for foreground and background tables. 73*4520Snw141292# 74*4520Snw141292# 2. The union of the foreground an background tables consists of N entries 75*4520Snw141292# where each entry an L-digit key. (Actually, some keys can be longer 76*4520Snw141292# than L characters, but they always start with L digits.) The keys 77*4520Snw141292# cover all integers between 1 and N. Whenever an entry is added to 78*4520Snw141292# the foreground it is removed form the background and vice versa. 79*4520Snw141292# 80*4520Snw141292# 3. Some entries in the foreground and background tables have keys that 81*4520Snw141292# begin with an L-digit number but are followed by additional characters. 82*4520Snw141292# For each such entry there is a corresponding entry in the long key 83*4520Snw141292# table. The long key table entry has a key which is just the L-digit 84*4520Snw141292# number and data which is the length of the key in the foreground and 85*4520Snw141292# background tables. 86*4520Snw141292# 87*4520Snw141292# 4. The data for both foreground and background entries is usually a 88*4520Snw141292# short string. But some entries have long data strings. For each 89*4520Snw141292# such entries there is an entry in the long data type. The key to 90*4520Snw141292# long data table is an L-digit number. (The extension on long keys 91*4520Snw141292# is omitted.) The data is the number of charaters in the data of the 92*4520Snw141292# foreground or background entry. 93*4520Snw141292# 94*4520Snw141292# The following function builds a database that satisfies all of the above 95*4520Snw141292# invariants. 96*4520Snw141292# 97*4520Snw141292proc build_db {N L} { 98*4520Snw141292 for {set i 2} {$i<=6} {incr i} { 99*4520Snw141292 catch {btree_close_cursor [set ::c$i]} 100*4520Snw141292 btree_clear_table $::b $i 101*4520Snw141292 set ::c$i [btree_cursor $::b $i 1] 102*4520Snw141292 } 103*4520Snw141292 btree_insert $::c2 N $N 104*4520Snw141292 btree_insert $::c2 L $L 105*4520Snw141292 set format %0${L}d 106*4520Snw141292 for {set i 1} {$i<=$N} {incr i} { 107*4520Snw141292 set key [format $format $i] 108*4520Snw141292 set data $key 109*4520Snw141292 btree_insert $::c3 $key $data 110*4520Snw141292 } 111*4520Snw141292} 112*4520Snw141292 113*4520Snw141292# Given a base key number and a length, construct the full text of the key 114*4520Snw141292# or data. 115*4520Snw141292# 116*4520Snw141292proc make_payload {keynum L len} { 117*4520Snw141292 set key [format %0${L}d $keynum] 118*4520Snw141292 set r $key 119*4520Snw141292 set i 1 120*4520Snw141292 while {[string length $r]<$len} { 121*4520Snw141292 append r " ($i) $key" 122*4520Snw141292 incr i 123*4520Snw141292 } 124*4520Snw141292 return [string range $r 0 [expr {$len-1}]] 125*4520Snw141292} 126*4520Snw141292 127*4520Snw141292# Verify the invariants on the database. Return an empty string on 128*4520Snw141292# success or an error message if something is amiss. 129*4520Snw141292# 130*4520Snw141292proc check_invariants {} { 131*4520Snw141292 set ck [btree_integrity_check $::b 2 3 4 5 6] 132*4520Snw141292 if {$ck!=""} { 133*4520Snw141292 puts "\n*** SANITY:\n$ck" 134*4520Snw141292 exit 135*4520Snw141292 return $ck 136*4520Snw141292 } 137*4520Snw141292 btree_move_to $::c3 {} 138*4520Snw141292 btree_move_to $::c4 {} 139*4520Snw141292 btree_move_to $::c2 N 140*4520Snw141292 set N [btree_data $::c2] 141*4520Snw141292 btree_move_to $::c2 L 142*4520Snw141292 set L [btree_data $::c2] 143*4520Snw141292 set LM1 [expr {$L-1}] 144*4520Snw141292 for {set i 1} {$i<=$N} {incr i} { 145*4520Snw141292 set key [btree_key $::c3] 146*4520Snw141292 if {[scan $key %d k]<1} {set k 0} 147*4520Snw141292 if {$k!=$i} { 148*4520Snw141292 set key [btree_key $::c4] 149*4520Snw141292 if {[scan $key %d k]<1} {set k 0} 150*4520Snw141292 if {$k!=$i} { 151*4520Snw141292 # puts "MISSING $i" 152*4520Snw141292 # puts {Page 3:}; btree_page_dump $::b 3 153*4520Snw141292 # puts {Page 4:}; btree_page_dump $::b 4 154*4520Snw141292 # exit 155*4520Snw141292 return "Key $i is missing from both foreground and background" 156*4520Snw141292 } 157*4520Snw141292 set data [btree_data $::c4] 158*4520Snw141292 btree_next $::c4 159*4520Snw141292 } else { 160*4520Snw141292 set data [btree_data $::c3] 161*4520Snw141292 btree_next $::c3 162*4520Snw141292 } 163*4520Snw141292 set skey [string range $key 0 $LM1] 164*4520Snw141292 if {[btree_move_to $::c5 $skey]==0} { 165*4520Snw141292 set keylen [btree_data $::c5] 166*4520Snw141292 } else { 167*4520Snw141292 set keylen $L 168*4520Snw141292 } 169*4520Snw141292 if {[string length $key]!=$keylen} { 170*4520Snw141292 return "Key $i is the wrong size.\ 171*4520Snw141292 Is \"$key\" but should be \"[make_payload $k $L $keylen]\"" 172*4520Snw141292 } 173*4520Snw141292 if {[make_payload $k $L $keylen]!=$key} { 174*4520Snw141292 return "Key $i has an invalid extension" 175*4520Snw141292 } 176*4520Snw141292 if {[btree_move_to $::c6 $skey]==0} { 177*4520Snw141292 set datalen [btree_data $::c6] 178*4520Snw141292 } else { 179*4520Snw141292 set datalen $L 180*4520Snw141292 } 181*4520Snw141292 if {[string length $data]!=$datalen} { 182*4520Snw141292 return "Data for $i is the wrong size.\ 183*4520Snw141292 Is [string length $data] but should be $datalen" 184*4520Snw141292 } 185*4520Snw141292 if {[make_payload $k $L $datalen]!=$data} { 186*4520Snw141292 return "Entry $i has an incorrect data" 187*4520Snw141292 } 188*4520Snw141292 } 189*4520Snw141292} 190*4520Snw141292 191*4520Snw141292# Make random changes to the database such that each change preserves 192*4520Snw141292# the invariants. The number of changes is $n*N where N is the parameter 193*4520Snw141292# from the descriptor table. Each changes begins with a random key. 194*4520Snw141292# the entry with that key is put in the foreground table with probability 195*4520Snw141292# $I and it is put in background with probability (1.0-$I). It gets 196*4520Snw141292# a long key with probability $K and long data with probability $D. 197*4520Snw141292# 198*4520Snw141292set chngcnt 0 199*4520Snw141292proc random_changes {n I K D} { 200*4520Snw141292 btree_move_to $::c2 N 201*4520Snw141292 set N [btree_data $::c2] 202*4520Snw141292 btree_move_to $::c2 L 203*4520Snw141292 set L [btree_data $::c2] 204*4520Snw141292 set LM1 [expr {$L-1}] 205*4520Snw141292 set total [expr {int($N*$n)}] 206*4520Snw141292 set format %0${L}d 207*4520Snw141292 for {set i 0} {$i<$total} {incr i} { 208*4520Snw141292 set k [expr {int(rand()*$N)+1}] 209*4520Snw141292 set insert [expr {rand()<=$I}] 210*4520Snw141292 set longkey [expr {rand()<=$K}] 211*4520Snw141292 set longdata [expr {rand()<=$D}] 212*4520Snw141292 # incr ::chngcnt 213*4520Snw141292 # if {$::chngcnt==251} {btree_tree_dump $::b 3} 214*4520Snw141292 # puts "CHANGE $::chngcnt: $k $insert $longkey $longdata" 215*4520Snw141292 if {$longkey} { 216*4520Snw141292 set x [expr {rand()}] 217*4520Snw141292 set keylen [expr {int($x*$x*$x*$x*3000)+10}] 218*4520Snw141292 } else { 219*4520Snw141292 set keylen $L 220*4520Snw141292 } 221*4520Snw141292 set key [make_payload $k $L $keylen] 222*4520Snw141292 if {$longdata} { 223*4520Snw141292 set x [expr {rand()}] 224*4520Snw141292 set datalen [expr {int($x*$x*$x*$x*3000)+10}] 225*4520Snw141292 } else { 226*4520Snw141292 set datalen $L 227*4520Snw141292 } 228*4520Snw141292 set data [make_payload $k $L $datalen] 229*4520Snw141292 set basekey [format $format $k] 230*4520Snw141292 if {[set c [btree_move_to $::c3 $basekey]]==0} { 231*4520Snw141292 btree_delete $::c3 232*4520Snw141292 } else { 233*4520Snw141292 if {$c<0} {btree_next $::c3} 234*4520Snw141292 if {[string match $basekey* [btree_key $::c3]]} { 235*4520Snw141292 btree_delete $::c3 236*4520Snw141292 } 237*4520Snw141292 } 238*4520Snw141292 if {[set c [btree_move_to $::c4 $basekey]]==0} { 239*4520Snw141292 btree_delete $::c4 240*4520Snw141292 } else { 241*4520Snw141292 if {$c<0} {btree_next $::c4} 242*4520Snw141292 if {[string match $basekey* [btree_key $::c4]]} { 243*4520Snw141292 btree_delete $::c4 244*4520Snw141292 } 245*4520Snw141292 } 246*4520Snw141292 if {[scan [btree_key $::c4] %d kx]<1} {set kx -1} 247*4520Snw141292 if {$kx==$k} { 248*4520Snw141292 btree_delete $::c4 249*4520Snw141292 } 250*4520Snw141292 if {$insert} { 251*4520Snw141292 btree_insert $::c3 $key $data 252*4520Snw141292 } else { 253*4520Snw141292 btree_insert $::c4 $key $data 254*4520Snw141292 } 255*4520Snw141292 if {$longkey} { 256*4520Snw141292 btree_insert $::c5 $basekey $keylen 257*4520Snw141292 } elseif {[btree_move_to $::c5 $basekey]==0} { 258*4520Snw141292 btree_delete $::c5 259*4520Snw141292 } 260*4520Snw141292 if {$longdata} { 261*4520Snw141292 btree_insert $::c6 $basekey $datalen 262*4520Snw141292 } elseif {[btree_move_to $::c6 $basekey]==0} { 263*4520Snw141292 btree_delete $::c6 264*4520Snw141292 } 265*4520Snw141292 # set ck [btree_integrity_check $::b 2 3 4 5 6] 266*4520Snw141292 # if {$ck!=""} { 267*4520Snw141292 # puts "\nSANITY CHECK FAILED!\n$ck" 268*4520Snw141292 # exit 269*4520Snw141292 # } 270*4520Snw141292 # puts "PAGE 3:"; btree_page_dump $::b 3 271*4520Snw141292 # puts "PAGE 4:"; btree_page_dump $::b 4 272*4520Snw141292 } 273*4520Snw141292} 274*4520Snw141292 275*4520Snw141292# Repeat this test sequence on database of various sizes 276*4520Snw141292# 277*4520Snw141292set testno 2 278*4520Snw141292foreach {N L} { 279*4520Snw141292 10 2 280*4520Snw141292 50 2 281*4520Snw141292 200 3 282*4520Snw141292 2000 5 283*4520Snw141292} { 284*4520Snw141292 puts "**** N=$N L=$L ****" 285*4520Snw141292 set hash [md5file test2.bt] 286*4520Snw141292 do_test btree2-$testno.1 [subst -nocommands { 287*4520Snw141292 set ::c2 [btree_cursor $::b 2 1] 288*4520Snw141292 set ::c3 [btree_cursor $::b 3 1] 289*4520Snw141292 set ::c4 [btree_cursor $::b 4 1] 290*4520Snw141292 set ::c5 [btree_cursor $::b 5 1] 291*4520Snw141292 set ::c6 [btree_cursor $::b 6 1] 292*4520Snw141292 btree_begin_transaction $::b 293*4520Snw141292 build_db $N $L 294*4520Snw141292 check_invariants 295*4520Snw141292 }] {} 296*4520Snw141292 do_test btree2-$testno.2 { 297*4520Snw141292 btree_close_cursor $::c2 298*4520Snw141292 btree_close_cursor $::c3 299*4520Snw141292 btree_close_cursor $::c4 300*4520Snw141292 btree_close_cursor $::c5 301*4520Snw141292 btree_close_cursor $::c6 302*4520Snw141292 btree_rollback $::b 303*4520Snw141292 md5file test2.bt 304*4520Snw141292 } $hash 305*4520Snw141292 do_test btree2-$testno.3 [subst -nocommands { 306*4520Snw141292 btree_begin_transaction $::b 307*4520Snw141292 set ::c2 [btree_cursor $::b 2 1] 308*4520Snw141292 set ::c3 [btree_cursor $::b 3 1] 309*4520Snw141292 set ::c4 [btree_cursor $::b 4 1] 310*4520Snw141292 set ::c5 [btree_cursor $::b 5 1] 311*4520Snw141292 set ::c6 [btree_cursor $::b 6 1] 312*4520Snw141292 build_db $N $L 313*4520Snw141292 check_invariants 314*4520Snw141292 }] {} 315*4520Snw141292 do_test btree2-$testno.4 { 316*4520Snw141292 btree_commit $::b 317*4520Snw141292 check_invariants 318*4520Snw141292 } {} 319*4520Snw141292 do_test btree2-$testno.5 { 320*4520Snw141292 lindex [btree_pager_stats $::b] 1 321*4520Snw141292 } {6} 322*4520Snw141292 do_test btree2-$testno.6 { 323*4520Snw141292 btree_close_cursor $::c2 324*4520Snw141292 btree_close_cursor $::c3 325*4520Snw141292 btree_close_cursor $::c4 326*4520Snw141292 btree_close_cursor $::c5 327*4520Snw141292 btree_close_cursor $::c6 328*4520Snw141292 lindex [btree_pager_stats $::b] 1 329*4520Snw141292 } {0} 330*4520Snw141292 do_test btree2-$testno.7 { 331*4520Snw141292 btree_close $::b 332*4520Snw141292 } {} 333*4520Snw141292after 100 334*4520Snw141292 # For each database size, run various changes tests. 335*4520Snw141292 # 336*4520Snw141292 set num2 1 337*4520Snw141292 foreach {n I K D} { 338*4520Snw141292 0.5 0.5 0.1 0.1 339*4520Snw141292 1.0 0.2 0.1 0.1 340*4520Snw141292 1.0 0.8 0.1 0.1 341*4520Snw141292 2.0 0.0 0.1 0.1 342*4520Snw141292 2.0 1.0 0.1 0.1 343*4520Snw141292 2.0 0.0 0.0 0.0 344*4520Snw141292 2.0 1.0 0.0 0.0 345*4520Snw141292 } { 346*4520Snw141292 set testid btree2-$testno.8.$num2 347*4520Snw141292 set hash [md5file test2.bt] 348*4520Snw141292 do_test $testid.0 { 349*4520Snw141292 set ::b [btree_open test2.bt] 350*4520Snw141292 set ::c2 [btree_cursor $::b 2 1] 351*4520Snw141292 set ::c3 [btree_cursor $::b 3 1] 352*4520Snw141292 set ::c4 [btree_cursor $::b 4 1] 353*4520Snw141292 set ::c5 [btree_cursor $::b 5 1] 354*4520Snw141292 set ::c6 [btree_cursor $::b 6 1] 355*4520Snw141292 check_invariants 356*4520Snw141292 } {} 357*4520Snw141292 set cnt 6 358*4520Snw141292 for {set i 2} {$i<=6} {incr i} { 359*4520Snw141292 if {[lindex [btree_cursor_dump [set ::c$i]] 0]!=$i} {incr cnt} 360*4520Snw141292 } 361*4520Snw141292 do_test $testid.1 { 362*4520Snw141292 btree_begin_transaction $::b 363*4520Snw141292 lindex [btree_pager_stats $::b] 1 364*4520Snw141292 } $cnt 365*4520Snw141292 # exec cp test2.bt test2.bt.bu1 366*4520Snw141292 do_test $testid.2 [subst { 367*4520Snw141292 random_changes $n $I $K $D 368*4520Snw141292 }] {} 369*4520Snw141292 do_test $testid.3 { 370*4520Snw141292 check_invariants 371*4520Snw141292 } {} 372*4520Snw141292 do_test $testid.4 { 373*4520Snw141292 btree_close_cursor $::c2 374*4520Snw141292 btree_close_cursor $::c3 375*4520Snw141292 btree_close_cursor $::c4 376*4520Snw141292 btree_close_cursor $::c5 377*4520Snw141292 btree_close_cursor $::c6 378*4520Snw141292 btree_rollback $::b 379*4520Snw141292 md5file test2.bt 380*4520Snw141292 } $hash 381*4520Snw141292 # exec cp test2.bt test2.bt.bu2 382*4520Snw141292 btree_begin_transaction $::b 383*4520Snw141292 set ::c2 [btree_cursor $::b 2 1] 384*4520Snw141292 set ::c3 [btree_cursor $::b 3 1] 385*4520Snw141292 set ::c4 [btree_cursor $::b 4 1] 386*4520Snw141292 set ::c5 [btree_cursor $::b 5 1] 387*4520Snw141292 set ::c6 [btree_cursor $::b 6 1] 388*4520Snw141292 do_test $testid.5 [subst { 389*4520Snw141292 random_changes $n $I $K $D 390*4520Snw141292 }] {} 391*4520Snw141292 do_test $testid.6 { 392*4520Snw141292 check_invariants 393*4520Snw141292 } {} 394*4520Snw141292 do_test $testid.7 { 395*4520Snw141292 btree_commit $::b 396*4520Snw141292 check_invariants 397*4520Snw141292 } {} 398*4520Snw141292 set hash [md5file test2.bt] 399*4520Snw141292 do_test $testid.8 { 400*4520Snw141292 btree_close_cursor $::c2 401*4520Snw141292 btree_close_cursor $::c3 402*4520Snw141292 btree_close_cursor $::c4 403*4520Snw141292 btree_close_cursor $::c5 404*4520Snw141292 btree_close_cursor $::c6 405*4520Snw141292 lindex [btree_pager_stats $::b] 1 406*4520Snw141292 } {0} 407*4520Snw141292 do_test $testid.9 { 408*4520Snw141292 btree_close $::b 409*4520Snw141292 set ::b [btree_open test2.bt] 410*4520Snw141292 set ::c2 [btree_cursor $::b 2 1] 411*4520Snw141292 set ::c3 [btree_cursor $::b 3 1] 412*4520Snw141292 set ::c4 [btree_cursor $::b 4 1] 413*4520Snw141292 set ::c5 [btree_cursor $::b 5 1] 414*4520Snw141292 set ::c6 [btree_cursor $::b 6 1] 415*4520Snw141292 check_invariants 416*4520Snw141292 } {} 417*4520Snw141292 do_test $testid.10 { 418*4520Snw141292 btree_close_cursor $::c2 419*4520Snw141292 btree_close_cursor $::c3 420*4520Snw141292 btree_close_cursor $::c4 421*4520Snw141292 btree_close_cursor $::c5 422*4520Snw141292 btree_close_cursor $::c6 423*4520Snw141292 lindex [btree_pager_stats $::b] 1 424*4520Snw141292 } {0} 425*4520Snw141292 do_test $testid.11 { 426*4520Snw141292 btree_close $::b 427*4520Snw141292 } {} 428*4520Snw141292 incr num2 429*4520Snw141292 } 430*4520Snw141292 incr testno 431*4520Snw141292 set ::b [btree_open test2.bt] 432*4520Snw141292} 433*4520Snw141292 434*4520Snw141292# Testing is complete. Shut everything down. 435*4520Snw141292# 436*4520Snw141292do_test btree-999.1 { 437*4520Snw141292 lindex [btree_pager_stats $::b] 1 438*4520Snw141292} {0} 439*4520Snw141292do_test btree-999.2 { 440*4520Snw141292 btree_close $::b 441*4520Snw141292} {} 442*4520Snw141292do_test btree-999.3 { 443*4520Snw141292 file delete -force test2.bt 444*4520Snw141292 file exists test2.bt-journal 445*4520Snw141292} {0} 446*4520Snw141292 447*4520Snw141292} ;# end if( not mem: and has pager_open command ); 448*4520Snw141292 449*4520Snw141292finish_test 450