2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A# The author disclaims copyright to this source code. In place of
2N/A# a legal notice, here is a blessing:
2N/A# May you do good and not evil.
2N/A# May you find forgiveness for yourself and forgive others.
2N/A# May you share freely, never taking more than you give.
2N/A#***********************************************************************
2N/A# This file implements regression tests for SQLite library. The
2N/A# focus of this script is btree database backend
2N/Aset testdir [file dirname $argv0]
2N/Aif {[info commands btree_open]!="" && $SQLITE_PAGE_SIZE==1024
2N/A && $SQLITE_USABLE_SIZE==1024} {
2N/A# Basic functionality. Open and close a database.
2N/A# The second element of the list returned by btree_pager_stats is the
2N/A# number of pages currently checked out. We'll be checking this value
2N/A# frequently during this test script, to make sure the btree library
2N/A# is properly releasing the pages it checks out, and thus avoiding
2N/Ado_test btree-1.1.1 {
2N/A lindex [btree_pager_stats $::b1] 1
2N/A set rc [catch {btree_close $::b2} msg]
2N/A# Do an insert and verify that the database file grows in size.
2N/A set rc [catch {btree_begin_transaction $::b1} msg]
2N/Ado_test btree-1.4.1 {
2N/A lindex [btree_pager_stats $::b1] 1
2N/A set rc [catch {btree_cursor $::b1 2 1} ::c1]
2N/A if {$rc} {lappend rc $::c1}
2N/A set rc [catch {btree_insert $::c1 one 1.00} msg]
2N/A set rc [catch {btree_close_cursor $::c1} msg]
2N/A set rc [catch {btree_commit $::b1} msg]
2N/A lindex [btree_pager_stats $::b1] 1
2N/A# Reopen the database and attempt to read the record that we wrote.
2N/A set rc [catch {btree_cursor $::b1 2 1} ::c1]
2N/A if {$rc} {lappend rc $::c1}
2N/A btree_move_to $::c1 abc
2N/A btree_move_to $::c1 xyz
2N/A btree_move_to $::c1 one
2N/A lindex [btree_pager_stats $::b1] 1
2N/A# Do some additional inserts
2N/A btree_begin_transaction $::b1
2N/A btree_insert $::c1 two 2.00
2N/Ado_test btree-3.1.1 {
2N/A lindex [btree_pager_stats $::b1] 1
2N/A btree_insert $::c1 three 3.00
2N/A btree_insert $::c1 four 4.00
2N/A btree_insert $::c1 five 5.00
2N/A btree_insert $::c1 six 6.00
2N/A#btree_page_dump $::b1 2
2N/A set rc [btree_move_to $::c1 {}]
2N/A# Commit the changes, reopen and reread the data
2N/A set rc [catch {btree_close_cursor $::c1} msg]
2N/Ado_test btree-3.22.1 {
2N/A lindex [btree_pager_stats $::b1] 1
2N/A set rc [catch {btree_commit $::b1} msg]
2N/Ado_test btree-3.23.1 {
2N/A lindex [btree_pager_stats $::b1] 1
2N/A set rc [catch {btree_cursor $::b1 2 1} ::c1]
2N/A if {$rc} {lappend rc $::c1}
2N/Ado_test btree-3.25.1 {
2N/A lindex [btree_pager_stats $::b1] 1
2N/A set rc [btree_move_to $::c1 {}]
2N/A lindex [btree_pager_stats $::b1] 1
2N/A btree_begin_transaction $::b1
2N/A btree_move_to $::c1 one
2N/Ado_test btree-4.1.1 {
2N/A lindex [btree_pager_stats $::b1] 1
2N/A btree_move_to $::c1 {}
2N/A set key [btree_key $::c1]
2N/A lappend r [btree_data $::c1]
2N/A} {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
2N/A# Commit and make sure the delete is still there.
2N/A btree_move_to $::c1 {}
2N/A set key [btree_key $::c1]
2N/A lappend r [btree_data $::c1]
2N/A} {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
2N/A# Completely close the database and reopen it. Then check
2N/A lindex [btree_pager_stats $::b1] 1
2N/A btree_close_cursor $::c1
2N/A lindex [btree_pager_stats $::b1] 1
2N/A set ::c1 [btree_cursor $::b1 2 1]
2N/A lindex [btree_pager_stats $::b1] 1
2N/A set key [btree_key $::c1]
2N/A lappend r [btree_data $::c1]
2N/A} {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
2N/A# Try to read and write meta data
2N/A btree_get_meta $::b1
2N/A} {0 0 0 0 0 0 0 0 0 0}
2N/A set rc [catch {btree_update_meta $::b1 1 2 3 4 5 6 7 8 9 10} msg]
2N/A btree_begin_transaction $::b1
2N/A set rc [catch {btree_update_meta $::b1 1 2 3 4 5 6 7 8 9 10} msg]
2N/A btree_get_meta $::b1
2N/A} {0 2 3 4 5 6 7 8 9 10}
2N/A btree_close_cursor $::c1
2N/A btree_rollback $::b1
2N/A btree_get_meta $::b1
2N/A} {0 0 0 0 0 0 0 0 0 0}
2N/A btree_begin_transaction $::b1
2N/A btree_update_meta $::b1 999 10 20 30 40 50 60 70 80 90
2N/A btree_get_meta $::b1
2N/A} {0 10 20 30 40 50 60 70 80 90}
2N/Aproc select_all {cursor} {
2N/A btree_move_to $cursor {}
2N/A set key [btree_key $cursor]
2N/A lappend r [btree_data $cursor]
2N/Aproc select_keys {cursor} {
2N/A btree_move_to $cursor {}
2N/A set key [btree_key $cursor]
2N/A# Try to create a new table in the database file
2N/A set rc [catch {btree_create_table $::b1} msg]
2N/A btree_begin_transaction $::b1
2N/A set ::t2 [btree_create_table $::b1]
2N/Ado_test btree-6.2.1 {
2N/A lindex [btree_pager_stats $::b1] 1
2N/Ado_test btree-6.2.2 {
2N/A set ::c2 [btree_cursor $::b1 $::t2 1]
2N/A lindex [btree_pager_stats $::b1] 1
2N/Ado_test btree-6.2.3 {
2N/A btree_insert $::c2 ten 10
2N/A set ::c1 [btree_cursor $::b1 2 1]
2N/A lindex [btree_pager_stats $::b1] 1
2N/Ado_test btree-6.3.1 {
2N/A} {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
2N/A#btree_page_dump $::b1 3
2N/A# Drop the new table, then create it again anew.
2N/A btree_begin_transaction $::b1
2N/A btree_close_cursor $::c2
2N/Ado_test btree-6.6.1 {
2N/A lindex [btree_pager_stats $::b1] 1
2N/A btree_drop_table $::b1 $::t2
2N/Ado_test btree-6.7.1 {
2N/A lindex [btree_get_meta $::b1] 0
2N/A set ::t2 [btree_create_table $::b1]
2N/Ado_test btree-6.8.1 {
2N/A lindex [btree_get_meta $::b1] 0
2N/A set ::c2 [btree_cursor $::b1 $::t2 1]
2N/A lindex [btree_pager_stats $::b1] 1
2N/Ado_test btree-6.9.1 {
2N/A btree_move_to $::c2 {}
2N/A# If we drop table 2 it just clears the table. Table 2 always exists.
2N/A btree_close_cursor $::c1
2N/A btree_drop_table $::b1 2
2N/A set ::c1 [btree_cursor $::b1 2 1]
2N/A btree_move_to $::c1 {}
2N/A btree_close_cursor $::c2
2N/A lindex [btree_pager_stats $::b1] 1
2N/A# Check to see that pages defragment properly. To do this test we will
2N/A# 1. Fill the first page table 2 with data.
2N/A# 2. Delete every other entry of table 2.
2N/A# 3. Insert a single entry that requires more contiguous
2N/A# space than is available.
2N/A btree_begin_transaction $::b1
2N/A for {set i 0} {$i<36} {incr i} {
2N/A set key [format %03d $i]
2N/A set data "*** $key ***"
2N/A btree_insert $::c1 $key $data
2N/A lrange [btree_cursor_dump $::c1] 4 5
2N/A btree_move_to $::c1 000
2N/A while {[btree_key $::c1]!=""} {
2N/A lrange [btree_cursor_dump $::c1] 4 5
2N/A#btree_page_dump $::b1 2
2N/A btree_insert $::c1 018 {*** 018 ***+++}
2N/A lrange [btree_cursor_dump $::c1] 4 5
2N/A#btree_page_dump $::b1 2
2N/A# Delete an entry to make a hole of a known size, then immediately recreate
2N/A# that entry. This tests the path into allocateSpace where the hole exactly
2N/A# matches the size of the desired space.
2N/A btree_move_to $::c1 007
2N/A btree_move_to $::c1 011
2N/A lindex [btree_cursor_dump $::c1] 5
2N/A#btree_page_dump $::b1 2
2N/A btree_insert $::c1 007 {*** 007 ***}
2N/A lindex [btree_cursor_dump $::c1] 5
2N/A#btree_page_dump $::b1 2
2N/A# Make sure the freeSpace() routine properly coaleses adjacent memory blocks
2N/A btree_move_to $::c1 013
2N/A lrange [btree_cursor_dump $::c1] 4 5
2N/A btree_move_to $::c1 009
2N/A lrange [btree_cursor_dump $::c1] 4 5
2N/A btree_move_to $::c1 018
2N/A lrange [btree_cursor_dump $::c1] 4 5
2N/A btree_move_to $::c1 033
2N/A lrange [btree_cursor_dump $::c1] 4 5
2N/A btree_move_to $::c1 035
2N/A lrange [btree_cursor_dump $::c1] 4 5
2N/A#btree_page_dump $::b1 2
2N/A lindex [btree_pager_stats $::b1] 1
2N/A# Check to see that data on overflow pages work correctly.
2N/A set data "*** This is a very long key "
2N/A while {[string length $data]<256} {append data $data}
2N/A btree_insert $::c1 020 $data
2N/A#btree_page_dump $::b1 2
2N/Ado_test btree-8.1.1 {
2N/A lindex [btree_pager_stats $::b1] 1
2N/A#btree_pager_ref_dump $::b1
2N/A string length [btree_data $::c1]
2N/A} [string length $::data]
2N/Ado_test btree-8.4.1 {
2N/A lindex [btree_get_meta $::b1] 0
2N/A} [expr {int(([string length $::data]-238+1019)/1020)}]
2N/A set data "*** This is an even longer key"
2N/A while {[string length $data]<2000} {append data $data}
2N/A btree_insert $::c1 020 $data
2N/A string length [btree_data $::c1]
2N/A} [string length $::data]
2N/A btree_close_cursor $::c1
2N/A set ::c1 [btree_cursor $::b1 2 1]
2N/A btree_move_to $::c1 020
2N/A btree_begin_transaction $::b1
2N/A lindex [btree_get_meta $::b1] 0
2N/A} [expr {int(([string length $::data]-238+1019)/1020)}]
2N/A# Now check out keys on overflow pages.
2N/A set ::keyprefix "This is a long prefix to a key "
2N/A while {[string length $::keyprefix]<256} {append ::keyprefix $::keyprefix}
2N/A btree_close_cursor $::c1
2N/A btree_drop_table $::b1 2
2N/A lindex [btree_get_meta $::b1] 0
2N/Ado_test btree-8.12.1 {
2N/A set ::c1 [btree_cursor $::b1 2 1]
2N/A btree_insert $::c1 ${::keyprefix}1 1
2N/A btree_insert $::c1 ${::keyprefix}2 2
2N/A btree_insert $::c1 ${::keyprefix}3 3
2N/A btree_move_to $::c1 ${::keyprefix}2
2N/A btree_move_to $::c1 ${::keyprefix}1
2N/A btree_move_to $::c1 ${::keyprefix}3
2N/A lindex [btree_get_meta $::b1] 0
2N/A btree_move_to $::c1 ${::keyprefix}2
2N/A#btree_page_dump $::b1 2
2N/A#btree_page_dump $::b1 2
2N/A lindex [btree_get_meta $::b1] 0
2N/A lindex [btree_pager_stats $::b1] 1
2N/A btree_close_cursor $::c1
2N/A btree_drop_table $::b1 2
2N/A set ::c1 [btree_cursor $::b1 2 1]
2N/A lindex [btree_get_meta $::b1] 0
2N/A lindex [btree_pager_stats $::b1] 1
2N/A#btree_pager_ref_dump $::b1
2N/A# Check page splitting logic
2N/A for {set i 1} {$i<=19} {incr i} {
2N/A set key [format %03d $i]
2N/A set data "*** $key *** $key *** $key *** $key ***"
2N/A btree_insert $::c1 $key $data
2N/A#btree_tree_dump $::b1 2
2N/A#btree_pager_ref_dump $::b1
2N/A#set pager_refinfo_enable 1
2N/A btree_insert $::c1 020 {*** 020 *** 020 *** 020 *** 020 ***}
2N/A} {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020}
2N/A#btree_page_dump $::b1 5
2N/A#btree_page_dump $::b1 2
2N/A#btree_page_dump $::b1 7
2N/A#btree_pager_ref_dump $::b1
2N/A#set pager_refinfo_enable 0
2N/A# The previous "select_keys" command left the cursor pointing at the root
2N/A# page. So there should only be two pages checked out. 2 (the root) and
2N/Ado_test btree-9.2.1 {
2N/A lindex [btree_pager_stats $::b1] 1
2N/Afor {set i 1} {$i<=20} {incr i} {
2N/A do_test btree-9.3.$i.1 [subst {
2N/A btree_move_to $::c1 [format %03d $i]
2N/A do_test btree-9.3.$i.2 [subst {
2N/A btree_move_to $::c1 [format %03d $i]
2N/A string range \[btree_data $::c1\] 0 10
2N/A }] "*** [format %03d $i] ***"
2N/Ado_test btree-9.4.1 {
2N/A lindex [btree_pager_stats $::b1] 1
2N/A# Check the page joining logic.
2N/A#btree_page_dump $::b1 2
2N/A#btree_pager_ref_dump $::b1
2N/Ado_test btree-9.4.2 {
2N/A btree_move_to $::c1 005
2N/A#btree_page_dump $::b1 2
2N/Afor {set i 1} {$i<=19} {incr i} {
2N/A do_test btree-9.5.$i.1 [subst {
2N/A btree_move_to $::c1 [format %03d $i]
2N/A do_test btree-9.5.$i.2 [subst {
2N/A btree_move_to $::c1 [format %03d $i]
2N/A string range \[btree_data $::c1\] 0 10
2N/A }] "*** [format %03d $i] ***"
2N/A#btree_pager_ref_dump $::b1
2N/A btree_close_cursor $::c1
2N/A lindex [btree_pager_stats $::b1] 1
2N/A btree_rollback $::b1
2N/A lindex [btree_pager_stats $::b1] 1
2N/A# Create a tree of depth two. That is, there is a single divider entry
2N/A# on the root pages and two leaf pages. Then delete the divider entry
2N/A btree_begin_transaction $::b1
2N/A btree_drop_table $::b1 2
2N/A lindex [btree_pager_stats $::b1] 1
2N/A set ::c1 [btree_cursor $::b1 2 1]
2N/A lindex [btree_pager_stats $::b1] 1
2N/A for {set i 1} {$i<=20} {incr i} {
2N/A set key [format %03d $i]
2N/A set data "*** $key *** $key *** $key *** $key ***"
2N/A btree_insert $::c1 $key $data
2N/A} {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020}
2N/A#btree_page_dump $::b1 7
2N/A#btree_page_dump $::b1 2
2N/A#btree_page_dump $::b1 6
2N/A btree_move_to $::c1 011
2N/A} {001 002 003 004 005 006 007 008 009 010 012 013 014 015 016 017 018 019 020}
2N/A#btree_tree_dump $::b1 2
2N/A#btree_pager_ref_dump $::b1
2N/Afor {set i 1} {$i<=20} {incr i} {
2N/A do_test btree-10.5.$i {
2N/A btree_move_to $::c1 [format %03d $i]
2N/A lindex [btree_pager_stats $::b1] 1
2N/A #btree_pager_ref_dump $::b1
2N/A #btree_tree_dump $::b1 2
2N/A# Create a tree with lots more pages
2N/Afor {set i 21} {$i<=1000} {incr i} {
2N/A do_test btree-11.1.$i.1 {
2N/A set key [format %03d $i]
2N/A set ::data "*** $key *** $key *** $key *** $key ***"
2N/A btree_insert $::c1 $key $data
2N/A do_test btree-11.1.$i.2 {
2N/A set ::key [format %03d [expr {$i/2}]]
2N/A if {$::key=="011"} {set ::key 010}
2N/A do_test btree-11.1.$i.3 {
2N/A btree_move_to $::c1 $::key
2N/A# Make sure our reference count is still correct.
2N/A btree_close_cursor $::c1
2N/A lindex [btree_pager_stats $::b1] 1
2N/A set ::c1 [btree_cursor $::b1 2 1]
2N/A lindex [btree_pager_stats $::b1] 1
2N/A#btree_page_dump $::b1 2
2N/A# Delete the dividers on the root page
2N/A btree_move_to $::c1 257
2N/Ado_test btree-11.4.1 {
2N/A btree_move_to $::c1 256
2N/Ado_test btree-11.4.2 {
2N/A btree_move_to $::c1 258
2N/Ado_test btree-11.4.3 {
2N/A btree_move_to $::c1 259
2N/Ado_test btree-11.4.4 {
2N/A btree_move_to $::c1 257
2N/A set n [btree_key $::c1]
2N/A expr {$n==256||$n==258}
2N/A btree_move_to $::c1 513
2N/Ado_test btree-11.5.1 {
2N/A btree_move_to $::c1 512
2N/Ado_test btree-11.5.2 {
2N/A btree_move_to $::c1 514
2N/Ado_test btree-11.5.3 {
2N/A btree_move_to $::c1 515
2N/Ado_test btree-11.5.4 {
2N/A btree_move_to $::c1 513
2N/A set n [btree_key $::c1]
2N/A expr {$n==512||$n==514}
2N/A btree_move_to $::c1 769
2N/Ado_test btree-11.6.1 {
2N/A btree_move_to $::c1 768
2N/Ado_test btree-11.6.2 {
2N/A btree_move_to $::c1 771
2N/Ado_test btree-11.6.3 {
2N/A btree_move_to $::c1 770
2N/Ado_test btree-11.6.4 {
2N/A btree_move_to $::c1 769
2N/A set n [btree_key $::c1]
2N/A expr {$n==768||$n==770}
2N/A#btree_page_dump $::b1 2
2N/A#btree_page_dump $::b1 25
2N/A# Change the data on an intermediate node such that the node becomes overfull
2N/A# and has to split. We happen to know that intermediate nodes exist on
2N/A# 337, 401 and 465 by the btree_page_dumps above
2N/Aset ::data {This is going to be a very long data segment}
2N/Aappend ::data $::data
2N/Aappend ::data $::data
2N/A btree_insert $::c1 337 $::data
2N/A btree_insert $::c1 401 $::data
2N/A btree_insert $::c1 465 $::data
2N/A btree_move_to $::c1 337
2N/A btree_move_to $::c1 464
2N/Ado_test btree-12.10 {
2N/A btree_move_to $::c1 400
2N/Ado_test btree-12.11 {
2N/Ado_test btree-12.12 {
2N/A btree_integrity_check $::b1 2 3
2N/A# 1. Do some deletes from the 3-layer tree
2N/A# 2. Commit and reopen the database
2N/A# 3. Read every 15th entry and make sure it works
2N/A# 4. Implement btree_sanity and put it throughout this script
2N/Ado_test btree-15.98 {
2N/A btree_close_cursor $::c1
2N/A lindex [btree_pager_stats $::b1] 1
2N/Ado_test btree-15.99 {
2N/A btree_rollback $::b1
2N/A lindex [btree_pager_stats $::b1] 1
2N/Abtree_pager_ref_dump $::b1
2N/A} ;# end if( not mem: and has pager_open command );