| OLD | NEW | 
 | (Empty) | 
|    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. |  | 
|    4 # |  | 
|    5  |  | 
|    6 # Get the name of the database to analyze |  | 
|    7 # |  | 
|    8  |  | 
|    9 if {[llength $argv]!=2} { |  | 
|   10   puts stderr "Usage: $argv0 database-name table-or-index-name" |  | 
|   11   exit 1 |  | 
|   12 } |  | 
|   13 set file_to_analyze [lindex $argv 0] |  | 
|   14 if {![file exists $file_to_analyze]} { |  | 
|   15   puts stderr "No such file: $file_to_analyze" |  | 
|   16   exit 1 |  | 
|   17 } |  | 
|   18 if {![file readable $file_to_analyze]} { |  | 
|   19   puts stderr "File is not readable: $file_to_analyze" |  | 
|   20   exit 1 |  | 
|   21 } |  | 
|   22 if {[file size $file_to_analyze]<512} { |  | 
|   23   puts stderr "Empty or malformed database: $file_to_analyze" |  | 
|   24   exit 1 |  | 
|   25 } |  | 
|   26 set objname [lindex $argv 1] |  | 
|   27  |  | 
|   28 # Open the database |  | 
|   29 # |  | 
|   30 sqlite3 db [lindex $argv 0] |  | 
|   31 set DB [btree_open [lindex $argv 0] 1000 0] |  | 
|   32  |  | 
|   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  |  | 
|   39 # grandparent etc.) |  | 
|   40 # |  | 
|   41 # The following entries in that array are filled in with information retrieved |  | 
|   42 # using [btree_cursor_info]: |  | 
|   43 # |  | 
|   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 |  | 
|   54 #  |  | 
|   55 proc cursor_info {arrayvar csr {up 0}} { |  | 
|   56   upvar $arrayvar a |  | 
|   57   foreach [list a(page_no) \ |  | 
|   58                 a(entry_no) \ |  | 
|   59                 a(page_entries) \ |  | 
|   60                 a(cell_size) \ |  | 
|   61                 a(page_freebytes) \ |  | 
|   62                 a(page_freeblocks) \ |  | 
|   63                 a(payload_bytes) \ |  | 
|   64                 a(header_bytes) \ |  | 
|   65                 a(local_payload_bytes) \ |  | 
|   66                 a(parent) \ |  | 
|   67                 a(first_ovfl) ] [btree_cursor_info $csr $up] break |  | 
|   68 } |  | 
|   69  |  | 
|   70 # Determine the page-size of the database. This global variable is used |  | 
|   71 # throughout the script. |  | 
|   72 # |  | 
|   73 set pageSize [db eval {PRAGMA page_size}] |  | 
|   74  |  | 
|   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. |  | 
|   77 # |  | 
|   78 if {$objname=="sqlite_master"} { |  | 
|   79   set rootpage 1 |  | 
|   80   set type table |  | 
|   81 } else { |  | 
|   82   db eval { |  | 
|   83     SELECT rootpage, type FROM sqlite_master |  | 
|   84      WHERE name=$objname |  | 
|   85   } break |  | 
|   86   if {![info exists rootpage]} { |  | 
|   87     puts stderr "no such table or index: $objname" |  | 
|   88     exit 1 |  | 
|   89   } |  | 
|   90   if {$type!="table" && $type!="index"} { |  | 
|   91     puts stderr "$objname is something other than a table or index" |  | 
|   92     exit 1 |  | 
|   93   } |  | 
|   94   if {![string is integer -strict $rootpage]} { |  | 
|   95     puts stderr "invalid root page for $objname: $rootpage" |  | 
|   96     exit 1 |  | 
|   97   }  |  | 
|   98 } |  | 
|   99  |  | 
|  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. |  | 
|  103 #  |  | 
|  104 # If information about the page has been printed already, then |  | 
|  105 # this is a no-op. |  | 
|  106 #  |  | 
|  107 proc page_info {csr up} { |  | 
|  108   global seen |  | 
|  109   cursor_info ci $csr $up |  | 
|  110   set pg $ci(page_no) |  | 
|  111   if {[info exists seen($pg)]} return |  | 
|  112   set seen($pg) 1 |  | 
|  113  |  | 
|  114   # Do parent pages first |  | 
|  115   # |  | 
|  116   if {$ci(parent)} { |  | 
|  117     page_info $csr [expr {$up+1}] |  | 
|  118   } |  | 
|  119  |  | 
|  120   # Find the depth of this page |  | 
|  121   # |  | 
|  122   set depth 1 |  | 
|  123   set i $up |  | 
|  124   while {$ci(parent)} { |  | 
|  125     incr i |  | 
|  126     incr depth |  | 
|  127     cursor_info ci $csr $i |  | 
|  128   } |  | 
|  129  |  | 
|  130   # print the results |  | 
|  131   # |  | 
|  132   puts [format {LEVEL %d:  %6d} $depth $pg] |  | 
|  133 }   |  | 
|  134  |  | 
|  135    |  | 
|  136    |  | 
|  137  |  | 
|  138 # Loop through the object and print out page numbers |  | 
|  139 # |  | 
|  140 set csr [btree_cursor $DB $rootpage 0] |  | 
|  141 for {btree_first $csr} {![btree_eof $csr]} {btree_next $csr} { |  | 
|  142   page_info $csr 0 |  | 
|  143   set i 1 |  | 
|  144   foreach pg [btree_ovfl_info $DB $csr] { |  | 
|  145     puts [format {OVFL %3d: %6d} $i $pg] |  | 
|  146     incr i |  | 
|  147   } |  | 
|  148 } |  | 
|  149 exit 0 |  | 
| OLD | NEW |