1N/A#pragma ident "%Z%%M% %I% %E% SMI"
1N/A# The author disclaims copyright to this source code. In place of
1N/A# a legal notice, here is a blessing:
1N/A# May you do good and not evil.
1N/A# May you find forgiveness for yourself and forgive others.
1N/A# May you share freely, never taking more than you give.
1N/A#***********************************************************************
1N/A# This file implements regression tests for SQLite library. The
1N/A# focus of this script is btree database backend
1N/Aset testdir [file dirname $argv0]
1N/Aif {[info commands btree_open]!="" && $SQLITE_PAGE_SIZE==1024
1N/A && $SQLITE_USABLE_SIZE==1024} {
1N/A# Basic functionality. Open and close a database.
1N/A# The second element of the list returned by btree_pager_stats is the
1N/A# number of pages currently checked out. We'll be checking this value
1N/A# frequently during this test script, to make sure the btree library
1N/A# is properly releasing the pages it checks out, and thus avoiding
1N/Ado_test btree-1.1.1 {
1N/A lindex [btree_pager_stats $::b1] 1
1N/A set rc [catch {btree_close $::b2} msg]
1N/A# Do an insert and verify that the database file grows in size.
1N/A set rc [catch {btree_begin_transaction $::b1} msg]
1N/Ado_test btree-1.4.1 {
1N/A lindex [btree_pager_stats $::b1] 1
1N/A set rc [catch {btree_cursor $::b1 2 1} ::c1]
1N/A if {$rc} {lappend rc $::c1}
1N/A set rc [catch {btree_insert $::c1 one 1.00} msg]
1N/A set rc [catch {btree_close_cursor $::c1} msg]
1N/A set rc [catch {btree_commit $::b1} msg]
1N/A lindex [btree_pager_stats $::b1] 1
# Reopen the database and attempt to read the record that we wrote.
set rc [catch {btree_cursor $::b1 2 1} ::c1]
if {$rc} {lappend rc $::c1}
lindex [btree_pager_stats $::b1] 1
# Do some additional inserts
btree_begin_transaction $::b1
btree_insert $::c1 two 2.00
lindex [btree_pager_stats $::b1] 1
btree_insert $::c1 three 3.00
btree_insert $::c1 four 4.00
btree_insert $::c1 five 5.00
btree_insert $::c1 six 6.00
set rc [btree_move_to $::c1 {}]
# Commit the changes, reopen and reread the data
set rc [catch {btree_close_cursor $::c1} msg]
lindex [btree_pager_stats $::b1] 1
set rc [catch {btree_commit $::b1} msg]
lindex [btree_pager_stats $::b1] 1
set rc [catch {btree_cursor $::b1 2 1} ::c1]
if {$rc} {lappend rc $::c1}
lindex [btree_pager_stats $::b1] 1
set rc [btree_move_to $::c1 {}]
lindex [btree_pager_stats $::b1] 1
btree_begin_transaction $::b1
lindex [btree_pager_stats $::b1] 1
set key [btree_key $::c1]
lappend r [btree_data $::c1]
} {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
# Commit and make sure the delete is still there.
set key [btree_key $::c1]
lappend r [btree_data $::c1]
} {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
# Completely close the database and reopen it. Then check
lindex [btree_pager_stats $::b1] 1
lindex [btree_pager_stats $::b1] 1
set ::c1 [btree_cursor $::b1 2 1]
lindex [btree_pager_stats $::b1] 1
set key [btree_key $::c1]
lappend r [btree_data $::c1]
} {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
# Try to read and write meta data
set rc [catch {btree_update_meta $::b1 1 2 3 4 5 6 7 8 9 10} msg]
btree_begin_transaction $::b1
set rc [catch {btree_update_meta $::b1 1 2 3 4 5 6 7 8 9 10} msg]
btree_begin_transaction $::b1
btree_update_meta $::b1 999 10 20 30 40 50 60 70 80 90
} {0 10 20 30 40 50 60 70 80 90}
proc select_all {cursor} {
set key [btree_key $cursor]
lappend r [btree_data $cursor]
proc select_keys {cursor} {
set key [btree_key $cursor]
# Try to create a new table in the database file
set rc [catch {btree_create_table $::b1} msg]
btree_begin_transaction $::b1
set ::t2 [btree_create_table $::b1]
lindex [btree_pager_stats $::b1] 1
set ::c2 [btree_cursor $::b1 $::t2 1]
lindex [btree_pager_stats $::b1] 1
btree_insert $::c2 ten 10
set ::c1 [btree_cursor $::b1 2 1]
lindex [btree_pager_stats $::b1] 1
} {five 5.00 four 4.00 six 6.00 three 3.00 two 2.00}
# Drop the new table, then create it again anew.
btree_begin_transaction $::b1
lindex [btree_pager_stats $::b1] 1
btree_drop_table $::b1 $::t2
lindex [btree_get_meta $::b1] 0
set ::t2 [btree_create_table $::b1]
lindex [btree_get_meta $::b1] 0
set ::c2 [btree_cursor $::b1 $::t2 1]
lindex [btree_pager_stats $::b1] 1
# If we drop table 2 it just clears the table. Table 2 always exists.
set ::c1 [btree_cursor $::b1 2 1]
lindex [btree_pager_stats $::b1] 1
# Check to see that pages defragment properly. To do this test we will
# 1. Fill the first page table 2 with data.
# 2. Delete every other entry of table 2.
# 3. Insert a single entry that requires more contiguous
# space than is available.
btree_begin_transaction $::b1
for {set i 0} {$i<36} {incr i} {
btree_insert $::c1 $key $data
lrange [btree_cursor_dump $::c1] 4 5
while {[btree_key $::c1]!=""} {
lrange [btree_cursor_dump $::c1] 4 5
btree_insert $::c1 018 {*** 018 ***+++}
lrange [btree_cursor_dump $::c1] 4 5
# Delete an entry to make a hole of a known size, then immediately recreate
# that entry. This tests the path into allocateSpace where the hole exactly
# matches the size of the desired space.
lindex [btree_cursor_dump $::c1] 5
btree_insert $::c1 007 {*** 007 ***}
lindex [btree_cursor_dump $::c1] 5
# Make sure the freeSpace() routine properly coaleses adjacent memory blocks
lrange [btree_cursor_dump $::c1] 4 5
lrange [btree_cursor_dump $::c1] 4 5
lrange [btree_cursor_dump $::c1] 4 5
lrange [btree_cursor_dump $::c1] 4 5
lrange [btree_cursor_dump $::c1] 4 5
lindex [btree_pager_stats $::b1] 1
# Check to see that data on overflow pages work correctly.
set data "*** This is a very long key "
while {[string length $data]<256} {append data $data}
btree_insert $::c1 020 $data
lindex [btree_pager_stats $::b1] 1
#btree_pager_ref_dump $::b1
string length [btree_data $::c1]
} [string length $::data]
lindex [btree_get_meta $::b1] 0
} [expr {int(([string length $::data]-238+1019)/1020)}]
set data "*** This is an even longer key"
while {[string length $data]<2000} {append data $data}
btree_insert $::c1 020 $data
string length [btree_data $::c1]
} [string length $::data]
set ::c1 [btree_cursor $::b1 2 1]
btree_begin_transaction $::b1
lindex [btree_get_meta $::b1] 0
} [expr {int(([string length $::data]-238+1019)/1020)}]
# Now check out keys on overflow pages.
set ::keyprefix "This is a long prefix to a key "
while {[string length $::keyprefix]<256} {append ::keyprefix $::keyprefix}
lindex [btree_get_meta $::b1] 0
set ::c1 [btree_cursor $::b1 2 1]
btree_insert $::c1 ${::keyprefix}1 1
btree_insert $::c1 ${::keyprefix}2 2
btree_insert $::c1 ${::keyprefix}3 3
btree_move_to $::c1 ${::keyprefix}2
btree_move_to $::c1 ${::keyprefix}1
btree_move_to $::c1 ${::keyprefix}3
lindex [btree_get_meta $::b1] 0
btree_move_to $::c1 ${::keyprefix}2
lindex [btree_get_meta $::b1] 0
lindex [btree_pager_stats $::b1] 1
set ::c1 [btree_cursor $::b1 2 1]
lindex [btree_get_meta $::b1] 0
lindex [btree_pager_stats $::b1] 1
#btree_pager_ref_dump $::b1
# Check page splitting logic
for {set i 1} {$i<=19} {incr i} {
set data "*** $key *** $key *** $key *** $key ***"
btree_insert $::c1 $key $data
#btree_pager_ref_dump $::b1
#set pager_refinfo_enable 1
btree_insert $::c1 020 {*** 020 *** 020 *** 020 *** 020 ***}
} {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020}
#btree_pager_ref_dump $::b1
#set pager_refinfo_enable 0
# The previous "select_keys" command left the cursor pointing at the root
# page. So there should only be two pages checked out. 2 (the root) and
lindex [btree_pager_stats $::b1] 1
for {set i 1} {$i<=20} {incr i} {
do_test btree-9.3.$i.1 [subst {
btree_move_to $::c1 [format %03d $i]
do_test btree-9.3.$i.2 [subst {
btree_move_to $::c1 [format %03d $i]
string range \[btree_data $::c1\] 0 10
}] "*** [format %03d $i] ***"
lindex [btree_pager_stats $::b1] 1
# Check the page joining logic.
#btree_pager_ref_dump $::b1
for {set i 1} {$i<=19} {incr i} {
do_test btree-9.5.$i.1 [subst {
btree_move_to $::c1 [format %03d $i]
do_test btree-9.5.$i.2 [subst {
btree_move_to $::c1 [format %03d $i]
string range \[btree_data $::c1\] 0 10
}] "*** [format %03d $i] ***"
#btree_pager_ref_dump $::b1
lindex [btree_pager_stats $::b1] 1
lindex [btree_pager_stats $::b1] 1
# 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
btree_begin_transaction $::b1
lindex [btree_pager_stats $::b1] 1
set ::c1 [btree_cursor $::b1 2 1]
lindex [btree_pager_stats $::b1] 1
for {set i 1} {$i<=20} {incr i} {
set data "*** $key *** $key *** $key *** $key ***"
btree_insert $::c1 $key $data
} {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020}
} {001 002 003 004 005 006 007 008 009 010 012 013 014 015 016 017 018 019 020}
#btree_pager_ref_dump $::b1
for {set i 1} {$i<=20} {incr i} {
btree_move_to $::c1 [format %03d $i]
lindex [btree_pager_stats $::b1] 1
#btree_pager_ref_dump $::b1
# Create a tree with lots more pages
for {set i 21} {$i<=1000} {incr i} {
do_test btree-11.1.$i.1 {
set ::data "*** $key *** $key *** $key *** $key ***"
btree_insert $::c1 $key $data
do_test btree-11.1.$i.2 {
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
# Make sure our reference count is still correct.
lindex [btree_pager_stats $::b1] 1
set ::c1 [btree_cursor $::b1 2 1]
lindex [btree_pager_stats $::b1] 1
# Delete the dividers on the root page
#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
set ::data {This is going to be a very long data segment}
btree_insert $::c1 337 $::data
btree_insert $::c1 401 $::data
btree_insert $::c1 465 $::data
btree_integrity_check $::b1 2 3
# 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
lindex [btree_pager_stats $::b1] 1
lindex [btree_pager_stats $::b1] 1
btree_pager_ref_dump $::b1
} ;# end if( not mem: and has pager_open command );