1 # Run this TCL script using "testfixture" to get a report that shows
2 # the sequence of database pages used by a particular table or index.
3 # This information is used for fragmentation analysis.
6 # Get the name of the database to analyze
9 if {[llength $argv]!=2} {
10 puts stderr
"Usage: $argv0 database-name table-or-index-name"
13 set file_to_analyze
[lindex $argv 0]
14 if {![file exists
$file_to_analyze]} {
15 puts stderr
"No such file: $file_to_analyze"
18 if {![file readable
$file_to_analyze]} {
19 puts stderr
"File is not readable: $file_to_analyze"
22 if {[file size
$file_to_analyze]<512} {
23 puts stderr
"Empty or malformed database: $file_to_analyze"
26 set objname
[lindex $argv 1]
30 sqlite3 db
[lindex $argv 0]
31 set DB
[btree_open
[lindex $argv 0] 1000 0]
33 # This proc is a wrapper around the btree_cursor_info command. The
34 # second argument is an open btree cursor returned by [btree_cursor].
35 # The first argument is the name of an array variable that exists in
36 # the scope of the caller. If the third argument is non-zero, then
37 # info is returned for the page that lies $up entries upwards in the
38 # tree-structure. (i.e. $up==1 returns the parent page, $up==2 the
41 # The following entries in that array are filled in with information retrieved
42 # using [btree_cursor_info]:
44 # $arrayvar(page_no) = The page number
45 # $arrayvar(entry_no) = The entry number
46 # $arrayvar(page_entries) = Total number of entries on this page
47 # $arrayvar(cell_size) = Cell size (local payload + header)
48 # $arrayvar(page_freebytes) = Number of free bytes on this page
49 # $arrayvar(page_freeblocks) = Number of free blocks on the page
50 # $arrayvar(payload_bytes) = Total payload size (local + overflow)
51 # $arrayvar(header_bytes) = Header size in bytes
52 # $arrayvar(local_payload_bytes) = Local payload size
53 # $arrayvar(parent) = Parent page number
55 proc cursor_info
{arrayvar csr
{up
0}} {
57 foreach [list a
(page_no
) \
65 a
(local_payload_bytes
) \
67 a
(first_ovfl
) ] [btree_cursor_info
$csr $up] break
70 # Determine the page-size of the database. This global variable is used
71 # throughout the script.
73 set pageSize
[db
eval {PRAGMA page_size
}]
75 # Find the root page of table or index to be analyzed. Also find out
76 # if the object is a table or an index.
78 if {$objname=="sqlite_master"} {
83 SELECT rootpage
, type FROM sqlite_master
86 if {![info exists rootpage
]} {
87 puts stderr
"no such table or index: $objname"
90 if {$type!="table" && $type!="index"} {
91 puts stderr
"$objname is something other than a table or index"
94 if {![string is integer
-strict $rootpage]} {
95 puts stderr
"invalid root page for $objname: $rootpage"
100 # The cursor $csr is pointing to an entry. Print out information
101 # about the page that $up levels above that page that contains
102 # the entry. If $up==0 use the page that contains the entry.
104 # If information about the page has been printed already, then
107 proc page_info
{csr up
} {
109 cursor_info ci
$csr $up
111 if {[info exists seen
($pg)]} return
114 # Do parent pages first
117 page_info
$csr [expr {$up+1}]
120 # Find the depth of this page
124 while {$ci(parent
)} {
127 cursor_info ci
$csr $i
132 puts [format {LEVEL
%d
: %6d
} $depth $pg]
138 # Loop through the object and print out page numbers
140 set csr
[btree_cursor
$DB $rootpage 0]
141 for {btree_first
$csr} {![btree_eof
$csr]} {btree_next
$csr} {
144 foreach pg
[btree_ovfl_info
$DB $csr] {
145 puts [format {OVFL
%3d
: %6d
} $i $pg]