2 #pragma ident "%Z%%M% %I% %E% SMI"
6 # The author disclaims copyright to this source code. In place of
7 # a legal notice, here is a blessing:
9 # May you do good and not evil.
10 # May you find forgiveness for yourself and forgive others.
11 # May you share freely, never taking more than you give.
13 #***********************************************************************
14 # This file implements regression tests for SQLite library. The
15 # focus of this script is btree database backend
17 # $Id: btree.test,v 1.15 2004/02/10 01:54:28 drh Exp $
20 set testdir [file dirname $argv0]
21 source $testdir/tester.tcl
23 if {[info commands btree_open]!="" && $SQLITE_PAGE_SIZE==1024
24 && $SQLITE_USABLE_SIZE==1024} {
26 # Basic functionality. Open and close a database.
29 file delete -force test1.bt
30 file delete -force test1.bt-journal
31 set rc [catch {btree_open test1.bt} ::b1]
34 # The second element of the list returned by btree_pager_stats is the
35 # number of pages currently checked out. We'll be checking this value
36 # frequently during this test script, to make sure the btree library
37 # is properly releasing the pages it checks out, and thus avoiding
41 lindex [btree_pager_stats $::b1] 1
44 set rc [catch {btree_open test1.bt} ::b2]
47 set rc [catch {btree_close $::b2} msg]
51 # Do an insert and verify that the database file grows in size.
54 set rc [catch {btree_begin_transaction $::b1} msg]
58 lindex [btree_pager_stats $::b1] 1
61 set rc [catch {btree_cursor $::b1 2 1} ::c1]
62 if {$rc} {lappend rc $::c1}
66 set rc [catch {btree_insert $::c1 one 1.00} msg]
76 set rc [catch {btree_close_cursor $::c1} msg]
80 set rc [catch {btree_commit $::b1} msg]
87 lindex [btree_pager_stats $::b1] 1
90 # Reopen the database and attempt to read the record that we wrote.
93 set rc [catch {btree_cursor $::b1 2 1} ::c1]
94 if {$rc} {lappend rc $::c1}
98 btree_move_to $::c1 abc
101 btree_move_to $::c1 xyz
104 btree_move_to $::c1 one
113 lindex [btree_pager_stats $::b1] 1
116 # Do some additional inserts
119 btree_begin_transaction $::b1
120 btree_insert $::c1 two 2.00
123 do_test btree-3.1.1 {
124 lindex [btree_pager_stats $::b1] 1
127 btree_insert $::c1 three 3.00
131 btree_insert $::c1 four 4.00
135 btree_insert $::c1 five 5.00
139 btree_insert $::c1 six 6.00
142 #btree_page_dump $::b1 2
144 set rc [btree_move_to $::c1 {}]
196 # Commit the changes, reopen and reread the data
199 set rc [catch {btree_close_cursor $::c1} msg]
202 do_test btree-3.22.1 {
203 lindex [btree_pager_stats $::b1] 1
206 set rc [catch {btree_commit $::b1} msg]
209 do_test btree-3.23.1 {
210 lindex [btree_pager_stats $::b1] 1
216 set rc [catch {btree_cursor $::b1 2 1} ::c1]
217 if {$rc} {lappend rc $::c1}
220 do_test btree-3.25.1 {
221 lindex [btree_pager_stats $::b1] 1
224 set rc [btree_move_to $::c1 {}]
276 lindex [btree_pager_stats $::b1] 1
283 btree_begin_transaction $::b1
284 btree_move_to $::c1 one
287 do_test btree-4.1.1 {
288 lindex [btree_pager_stats $::b1] 1
305 btree_move_to $::c1 {}
308 set key [btree_key $::c1]
311 lappend r [btree_data $::c1]
315 } {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
317 # Commit and make sure the delete is still there.
321 btree_move_to $::c1 {}
324 set key [btree_key $::c1]
327 lappend r [btree_data $::c1]
331 } {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
333 # Completely close the database and reopen it. Then check
337 lindex [btree_pager_stats $::b1] 1
340 btree_close_cursor $::c1
341 lindex [btree_pager_stats $::b1] 1
345 set ::b1 [btree_open test1.bt]
346 set ::c1 [btree_cursor $::b1 2 1]
347 lindex [btree_pager_stats $::b1] 1
353 set key [btree_key $::c1]
356 lappend r [btree_data $::c1]
360 } {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
362 # Try to read and write meta data
366 } {0 0 0 0 0 0 0 0 0 0}
368 set rc [catch {btree_update_meta $::b1 1 2 3 4 5 6 7 8 9 10} msg]
372 btree_begin_transaction $::b1
373 set rc [catch {btree_update_meta $::b1 1 2 3 4 5 6 7 8 9 10} msg]
378 } {0 2 3 4 5 6 7 8 9 10}
380 btree_close_cursor $::c1
383 } {0 0 0 0 0 0 0 0 0 0}
385 btree_begin_transaction $::b1
386 btree_update_meta $::b1 999 10 20 30 40 50 60 70 80 90
389 } {0 10 20 30 40 50 60 70 80 90}
391 proc select_all {cursor} {
393 btree_move_to $cursor {}
395 set key [btree_key $cursor]
398 lappend r [btree_data $cursor]
403 proc select_keys {cursor} {
405 btree_move_to $cursor {}
407 set key [btree_key $cursor]
415 # Try to create a new table in the database file
418 set rc [catch {btree_create_table $::b1} msg]
422 btree_begin_transaction $::b1
423 set ::t2 [btree_create_table $::b1]
425 do_test btree-6.2.1 {
426 lindex [btree_pager_stats $::b1] 1
428 do_test btree-6.2.2 {
429 set ::c2 [btree_cursor $::b1 $::t2 1]
430 lindex [btree_pager_stats $::b1] 1
432 do_test btree-6.2.3 {
433 btree_insert $::c2 ten 10
438 set ::c1 [btree_cursor $::b1 2 1]
439 lindex [btree_pager_stats $::b1] 1
441 do_test btree-6.3.1 {
443 } {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
444 #btree_page_dump $::b1 3
449 # Drop the new table, then create it again anew.
452 btree_begin_transaction $::b1
455 btree_close_cursor $::c2
457 do_test btree-6.6.1 {
458 lindex [btree_pager_stats $::b1] 1
461 btree_drop_table $::b1 $::t2
463 do_test btree-6.7.1 {
464 lindex [btree_get_meta $::b1] 0
467 set ::t2 [btree_create_table $::b1]
469 do_test btree-6.8.1 {
470 lindex [btree_get_meta $::b1] 0
473 set ::c2 [btree_cursor $::b1 $::t2 1]
474 lindex [btree_pager_stats $::b1] 1
477 do_test btree-6.9.1 {
478 btree_move_to $::c2 {}
482 # If we drop table 2 it just clears the table. Table 2 always exists.
485 btree_close_cursor $::c1
486 btree_drop_table $::b1 2
487 set ::c1 [btree_cursor $::b1 2 1]
488 btree_move_to $::c1 {}
499 btree_close_cursor $::c2
500 lindex [btree_pager_stats $::b1] 1
503 # Check to see that pages defragment properly. To do this test we will
505 # 1. Fill the first page table 2 with data.
506 # 2. Delete every other entry of table 2.
507 # 3. Insert a single entry that requires more contiguous
508 # space than is available.
511 btree_begin_transaction $::b1
516 for {set i 0} {$i<36} {incr i} {
517 set key [format %03d $i]
518 set data "*** $key ***"
519 btree_insert $::c1 $key $data
521 lrange [btree_cursor_dump $::c1] 4 5
524 btree_move_to $::c1 000
525 while {[btree_key $::c1]!=""} {
530 lrange [btree_cursor_dump $::c1] 4 5
532 #btree_page_dump $::b1 2
534 btree_insert $::c1 018 {*** 018 ***+++}
538 lrange [btree_cursor_dump $::c1] 4 5
540 #btree_page_dump $::b1 2
542 # Delete an entry to make a hole of a known size, then immediately recreate
543 # that entry. This tests the path into allocateSpace where the hole exactly
544 # matches the size of the desired space.
547 btree_move_to $::c1 007
549 btree_move_to $::c1 011
553 lindex [btree_cursor_dump $::c1] 5
555 #btree_page_dump $::b1 2
557 btree_insert $::c1 007 {*** 007 ***}
558 lindex [btree_cursor_dump $::c1] 5
560 #btree_page_dump $::b1 2
562 # Make sure the freeSpace() routine properly coaleses adjacent memory blocks
565 btree_move_to $::c1 013
567 lrange [btree_cursor_dump $::c1] 4 5
570 btree_move_to $::c1 009
572 lrange [btree_cursor_dump $::c1] 4 5
575 btree_move_to $::c1 018
577 lrange [btree_cursor_dump $::c1] 4 5
580 btree_move_to $::c1 033
582 lrange [btree_cursor_dump $::c1] 4 5
585 btree_move_to $::c1 035
587 lrange [btree_cursor_dump $::c1] 4 5
589 #btree_page_dump $::b1 2
591 lindex [btree_pager_stats $::b1] 1
594 # Check to see that data on overflow pages work correctly.
597 set data "*** This is a very long key "
598 while {[string length $data]<256} {append data $data}
600 btree_insert $::c1 020 $data
602 #btree_page_dump $::b1 2
603 do_test btree-8.1.1 {
604 lindex [btree_pager_stats $::b1] 1
606 #btree_pager_ref_dump $::b1
608 string length [btree_data $::c1]
609 } [string length $::data]
616 do_test btree-8.4.1 {
617 lindex [btree_get_meta $::b1] 0
618 } [expr {int(([string length $::data]-238+1019)/1020)}]
620 set data "*** This is an even longer key"
621 while {[string length $data]<2000} {append data $data}
623 btree_insert $::c1 020 $data
626 string length [btree_data $::c1]
627 } [string length $::data]
636 btree_close_cursor $::c1
638 set ::b1 [btree_open test1.bt]
639 set ::c1 [btree_cursor $::b1 2 1]
640 btree_move_to $::c1 020
644 btree_begin_transaction $::b1
648 lindex [btree_get_meta $::b1] 0
649 } [expr {int(([string length $::data]-238+1019)/1020)}]
651 # Now check out keys on overflow pages.
654 set ::keyprefix "This is a long prefix to a key "
655 while {[string length $::keyprefix]<256} {append ::keyprefix $::keyprefix}
656 btree_close_cursor $::c1
657 btree_drop_table $::b1 2
658 lindex [btree_get_meta $::b1] 0
660 do_test btree-8.12.1 {
661 set ::c1 [btree_cursor $::b1 2 1]
662 btree_insert $::c1 ${::keyprefix}1 1
669 btree_insert $::c1 ${::keyprefix}2 2
670 btree_insert $::c1 ${::keyprefix}3 3
674 btree_move_to $::c1 ${::keyprefix}2
678 btree_move_to $::c1 ${::keyprefix}1
682 btree_move_to $::c1 ${::keyprefix}3
686 lindex [btree_get_meta $::b1] 0
689 btree_move_to $::c1 ${::keyprefix}2
692 #btree_page_dump $::b1 2
698 #btree_page_dump $::b1 2
700 lindex [btree_get_meta $::b1] 0
703 lindex [btree_pager_stats $::b1] 1
706 btree_close_cursor $::c1
707 btree_drop_table $::b1 2
708 set ::c1 [btree_cursor $::b1 2 1]
709 lindex [btree_get_meta $::b1] 0
712 lindex [btree_pager_stats $::b1] 1
714 #btree_pager_ref_dump $::b1
716 # Check page splitting logic
719 for {set i 1} {$i<=19} {incr i} {
720 set key [format %03d $i]
721 set data "*** $key *** $key *** $key *** $key ***"
722 btree_insert $::c1 $key $data
725 #btree_tree_dump $::b1 2
726 #btree_pager_ref_dump $::b1
727 #set pager_refinfo_enable 1
729 btree_insert $::c1 020 {*** 020 *** 020 *** 020 *** 020 ***}
731 } {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020}
732 #btree_page_dump $::b1 5
733 #btree_page_dump $::b1 2
734 #btree_page_dump $::b1 7
735 #btree_pager_ref_dump $::b1
736 #set pager_refinfo_enable 0
738 # The previous "select_keys" command left the cursor pointing at the root
739 # page. So there should only be two pages checked out. 2 (the root) and
741 do_test btree-9.2.1 {
742 lindex [btree_pager_stats $::b1] 1
744 for {set i 1} {$i<=20} {incr i} {
745 do_test btree-9.3.$i.1 [subst {
746 btree_move_to $::c1 [format %03d $i]
749 do_test btree-9.3.$i.2 [subst {
750 btree_move_to $::c1 [format %03d $i]
751 string range \[btree_data $::c1\] 0 10
752 }] "*** [format %03d $i] ***"
754 do_test btree-9.4.1 {
755 lindex [btree_pager_stats $::b1] 1
758 # Check the page joining logic.
760 #btree_page_dump $::b1 2
761 #btree_pager_ref_dump $::b1
762 do_test btree-9.4.2 {
763 btree_move_to $::c1 005
766 #btree_page_dump $::b1 2
767 for {set i 1} {$i<=19} {incr i} {
769 do_test btree-9.5.$i.1 [subst {
770 btree_move_to $::c1 [format %03d $i]
773 do_test btree-9.5.$i.2 [subst {
774 btree_move_to $::c1 [format %03d $i]
775 string range \[btree_data $::c1\] 0 10
776 }] "*** [format %03d $i] ***"
778 #btree_pager_ref_dump $::b1
780 btree_close_cursor $::c1
781 lindex [btree_pager_stats $::b1] 1
785 lindex [btree_pager_stats $::b1] 1
788 # Create a tree of depth two. That is, there is a single divider entry
789 # on the root pages and two leaf pages. Then delete the divider entry
793 btree_begin_transaction $::b1
794 btree_drop_table $::b1 2
795 lindex [btree_pager_stats $::b1] 1
798 set ::c1 [btree_cursor $::b1 2 1]
799 lindex [btree_pager_stats $::b1] 1
802 for {set i 1} {$i<=20} {incr i} {
803 set key [format %03d $i]
804 set data "*** $key *** $key *** $key *** $key ***"
805 btree_insert $::c1 $key $data
808 } {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020}
809 #btree_page_dump $::b1 7
810 #btree_page_dump $::b1 2
811 #btree_page_dump $::b1 6
813 btree_move_to $::c1 011
816 } {001 002 003 004 005 006 007 008 009 010 012 013 014 015 016 017 018 019 020}
817 #btree_tree_dump $::b1 2
818 #btree_pager_ref_dump $::b1
819 for {set i 1} {$i<=20} {incr i} {
820 do_test btree-10.5.$i {
821 btree_move_to $::c1 [format %03d $i]
822 lindex [btree_pager_stats $::b1] 1
824 #btree_pager_ref_dump $::b1
825 #btree_tree_dump $::b1 2
828 # Create a tree with lots more pages
832 for {set i 21} {$i<=1000} {incr i} {
833 do_test btree-11.1.$i.1 {
834 set key [format %03d $i]
835 set ::data "*** $key *** $key *** $key *** $key ***"
836 btree_insert $::c1 $key $data
839 do_test btree-11.1.$i.2 {
842 set ::key [format %03d [expr {$i/2}]]
843 if {$::key=="011"} {set ::key 010}
844 do_test btree-11.1.$i.3 {
845 btree_move_to $::c1 $::key
852 # Make sure our reference count is still correct.
855 btree_close_cursor $::c1
856 lindex [btree_pager_stats $::b1] 1
859 set ::c1 [btree_cursor $::b1 2 1]
860 lindex [btree_pager_stats $::b1] 1
862 #btree_page_dump $::b1 2
864 # Delete the dividers on the root page
867 btree_move_to $::c1 257
872 do_test btree-11.4.1 {
873 btree_move_to $::c1 256
876 do_test btree-11.4.2 {
877 btree_move_to $::c1 258
880 do_test btree-11.4.3 {
881 btree_move_to $::c1 259
884 do_test btree-11.4.4 {
885 btree_move_to $::c1 257
886 set n [btree_key $::c1]
887 expr {$n==256||$n==258}
890 btree_move_to $::c1 513
895 do_test btree-11.5.1 {
896 btree_move_to $::c1 512
899 do_test btree-11.5.2 {
900 btree_move_to $::c1 514
903 do_test btree-11.5.3 {
904 btree_move_to $::c1 515
907 do_test btree-11.5.4 {
908 btree_move_to $::c1 513
909 set n [btree_key $::c1]
910 expr {$n==512||$n==514}
913 btree_move_to $::c1 769
918 do_test btree-11.6.1 {
919 btree_move_to $::c1 768
922 do_test btree-11.6.2 {
923 btree_move_to $::c1 771
926 do_test btree-11.6.3 {
927 btree_move_to $::c1 770
930 do_test btree-11.6.4 {
931 btree_move_to $::c1 769
932 set n [btree_key $::c1]
933 expr {$n==768||$n==770}
935 #btree_page_dump $::b1 2
936 #btree_page_dump $::b1 25
938 # Change the data on an intermediate node such that the node becomes overfull
939 # and has to split. We happen to know that intermediate nodes exist on
940 # 337, 401 and 465 by the btree_page_dumps above
943 set ::data {This is going to be a very long data segment}
944 append ::data $::data
945 append ::data $::data
947 btree_insert $::c1 337 $::data
951 btree_insert $::c1 401 $::data
955 btree_insert $::c1 465 $::data
959 btree_move_to $::c1 337
970 btree_move_to $::c1 464
981 do_test btree-12.10 {
982 btree_move_to $::c1 400
985 do_test btree-12.11 {
989 do_test btree-12.12 {
994 btree_integrity_check $::b1 2 3
999 # 1. Do some deletes from the 3-layer tree
1000 # 2. Commit and reopen the database
1001 # 3. Read every 15th entry and make sure it works
1002 # 4. Implement btree_sanity and put it throughout this script
1005 do_test btree-15.98 {
1006 btree_close_cursor $::c1
1007 lindex [btree_pager_stats $::b1] 1
1009 do_test btree-15.99 {
1010 btree_rollback $::b1
1011 lindex [btree_pager_stats $::b1] 1
1013 btree_pager_ref_dump $::b1
1015 do_test btree-99.1 {
1021 } ;# end if( not mem: and has pager_open command );