Annotation of embedaddon/sqlite3/tool/fragck.tcl, revision 1.1
1.1 ! misho 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
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>