btree2.test 13 KB

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