Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(730)

Side by Side Diff: third_party/sqlite/src/tool/spaceanal.tcl

Issue 949043002: Add //third_party/sqlite to dirs_to_snapshot, remove net_sql.patch (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/sqlite/src/tool/showwal.c ('k') | third_party/sqlite/src/tool/stack_usage.tcl » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Run this TCL script using "testfixture" in order get a report that shows 1 # Run this TCL script using "testfixture" in order get a report that shows
2 # how much disk space is used by a particular data to actually store data 2 # how much disk space is used by a particular data to actually store data
3 # versus how much space is unused. 3 # versus how much space is unused.
4 # 4 #
5 5
6 if {[catch { 6 if {[catch {
7
8 # Get the name of the database to analyze 7 # Get the name of the database to analyze
9 # 8 #
10 #set argv $argv0 9 proc usage {} {
11 if {[llength $argv]!=1} { 10 set argv0 [file rootname [file tail [info nameofexecutable]]]
12 puts stderr "Usage: $argv0 database-name" 11 puts stderr "Usage: $argv0 database-name"
13 exit 1 12 exit 1
14 } 13 }
15 set file_to_analyze [lindex $argv 0] 14 set file_to_analyze {}
16 if {![file exists $file_to_analyze]} { 15 set flags(-pageinfo) 0
17 puts stderr "No such file: $file_to_analyze" 16 set flags(-stats) 0
17 append argv {}
18 foreach arg $argv {
19 if {[regexp {^-+pageinfo$} $arg]} {
20 set flags(-pageinfo) 1
21 } elseif {[regexp {^-+stats$} $arg]} {
22 set flags(-stats) 1
23 } elseif {[regexp {^-} $arg]} {
24 puts stderr "Unknown option: $arg"
25 usage
26 } elseif {$file_to_analyze!=""} {
27 usage
28 } else {
29 set file_to_analyze $arg
30 }
31 }
32 if {$file_to_analyze==""} usage
33 set root_filename $file_to_analyze
34 regexp {^file:(//)?([^?]*)} $file_to_analyze all x1 root_filename
35 if {![file exists $root_filename]} {
36 puts stderr "No such file: $root_filename"
18 exit 1 37 exit 1
19 } 38 }
20 if {![file readable $file_to_analyze]} { 39 if {![file readable $root_filename]} {
21 puts stderr "File is not readable: $file_to_analyze" 40 puts stderr "File is not readable: $root_filename"
22 exit 1 41 exit 1
23 } 42 }
24 if {[file size $file_to_analyze]<512} { 43 set true_file_size [file size $root_filename]
25 puts stderr "Empty or malformed database: $file_to_analyze" 44 if {$true_file_size<512} {
45 puts stderr "Empty or malformed database: $root_filename"
26 exit 1 46 exit 1
27 } 47 }
28 48
49 # Compute the total file size assuming test_multiplexor is being used.
50 # Assume that SQLITE_ENABLE_8_3_NAMES might be enabled
51 #
52 set extension [file extension $root_filename]
53 set pattern $root_filename
54 append pattern {[0-3][0-9][0-9]}
55 foreach f [glob -nocomplain $pattern] {
56 incr true_file_size [file size $f]
57 set extension {}
58 }
59 if {[string length $extension]>=2 && [string length $extension]<=4} {
60 set pattern [file rootname $root_filename]
61 append pattern {.[0-3][0-9][0-9]}
62 foreach f [glob -nocomplain $pattern] {
63 incr true_file_size [file size $f]
64 }
65 }
66
29 # Open the database 67 # Open the database
30 # 68 #
31 sqlite3 db [lindex $argv 0] 69 if {[catch {sqlite3 db $file_to_analyze -uri 1} msg]} {
70 puts stderr "error trying to open $file_to_analyze: $msg"
71 exit 1
72 }
32 register_dbstat_vtab db 73 register_dbstat_vtab db
33 74
34 set pageSize [db one {PRAGMA page_size}] 75 db eval {SELECT count(*) FROM sqlite_master}
76 set pageSize [expr {wide([db one {PRAGMA page_size}])}]
35 77
36 #set DB [btree_open [lindex $argv 0] 1000 0] 78 if {$flags(-pageinfo)} {
79 db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
80 db eval {SELECT name, path, pageno FROM temp.stat ORDER BY pageno} {
81 puts "$pageno $name $path"
82 }
83 exit 0
84 }
85 if {$flags(-stats)} {
86 db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
87 puts "BEGIN;"
88 puts "CREATE TABLE stats("
89 puts " name STRING, /* Name of table or index */"
90 puts " path INTEGER, /* Path to page from root */"
91 puts " pageno INTEGER, /* Page number */"
92 puts " pagetype STRING, /* 'internal', 'leaf' or 'overflow' */"
93 puts " ncell INTEGER, /* Cells on page (0 for overflow) */"
94 puts " payload INTEGER, /* Bytes of payload on this page */"
95 puts " unused INTEGER, /* Bytes of unused space on this page */"
96 puts " mx_payload INTEGER, /* Largest payload size of all cells */"
97 puts " pgoffset INTEGER, /* Offset of page in file */"
98 puts " pgsize INTEGER /* Size of the page */"
99 puts ");"
100 db eval {SELECT quote(name) || ',' ||
101 quote(path) || ',' ||
102 quote(pageno) || ',' ||
103 quote(pagetype) || ',' ||
104 quote(ncell) || ',' ||
105 quote(payload) || ',' ||
106 quote(unused) || ',' ||
107 quote(mx_payload) || ',' ||
108 quote(pgoffset) || ',' ||
109 quote(pgsize) AS x FROM stat} {
110 puts "INSERT INTO stats VALUES($x);"
111 }
112 puts "COMMIT;"
113 exit 0
114 }
37 115
38 # In-memory database for collecting statistics. This script loops through 116 # In-memory database for collecting statistics. This script loops through
39 # the tables and indices in the database being analyzed, adding a row for each 117 # the tables and indices in the database being analyzed, adding a row for each
40 # to an in-memory database (for which the schema is shown below). It then 118 # to an in-memory database (for which the schema is shown below). It then
41 # queries the in-memory db to produce the space-analysis report. 119 # queries the in-memory db to produce the space-analysis report.
42 # 120 #
43 sqlite3 mem :memory: 121 sqlite3 mem :memory:
44 set tabledef\ 122 set tabledef {CREATE TABLE space_used(
45 {CREATE TABLE space_used(
46 name clob, -- Name of a table or index in the database file 123 name clob, -- Name of a table or index in the database file
47 tblname clob, -- Name of associated table 124 tblname clob, -- Name of associated table
48 is_index boolean, -- TRUE if it is an index, false for a table 125 is_index boolean, -- TRUE if it is an index, false for a table
49 nentry int, -- Number of entries in the BTree 126 nentry int, -- Number of entries in the BTree
50 leaf_entries int, -- Number of leaf entries 127 leaf_entries int, -- Number of leaf entries
51 payload int, -- Total amount of data stored in this table or index 128 payload int, -- Total amount of data stored in this table or index
52 ovfl_payload int, -- Total amount of data stored on overflow pages 129 ovfl_payload int, -- Total amount of data stored on overflow pages
53 ovfl_cnt int, -- Number of entries that use overflow 130 ovfl_cnt int, -- Number of entries that use overflow
54 mx_payload int, -- Maximum payload size 131 mx_payload int, -- Maximum payload size
55 int_pages int, -- Number of interior pages used 132 int_pages int, -- Number of interior pages used
56 leaf_pages int, -- Number of leaf pages used 133 leaf_pages int, -- Number of leaf pages used
57 ovfl_pages int, -- Number of overflow pages used 134 ovfl_pages int, -- Number of overflow pages used
58 int_unused int, -- Number of unused bytes on interior pages 135 int_unused int, -- Number of unused bytes on interior pages
59 leaf_unused int, -- Number of unused bytes on primary pages 136 leaf_unused int, -- Number of unused bytes on primary pages
60 ovfl_unused int, -- Number of unused bytes on overflow pages 137 ovfl_unused int, -- Number of unused bytes on overflow pages
61 gap_cnt int -- Number of gaps in the page layout 138 gap_cnt int, -- Number of gaps in the page layout
139 compressed_size int -- Total bytes stored on disk
62 );} 140 );}
63 mem eval $tabledef 141 mem eval $tabledef
64 142
65 # Create a temporary "dbstat" virtual table. 143 # Create a temporary "dbstat" virtual table.
66 # 144 #
67 db eval { 145 db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat}
68 CREATE VIRTUAL TABLE temp.stat USING dbstat; 146 db eval {CREATE TEMP TABLE dbstat AS SELECT * FROM temp.stat
69 CREATE TEMP TABLE dbstat AS SELECT * FROM temp.stat ORDER BY name, path; 147 ORDER BY name, path}
70 DROP TABLE temp.stat; 148 db eval {DROP TABLE temp.stat}
71 }
72 149
73 proc isleaf {pagetype is_index} { 150 proc isleaf {pagetype is_index} {
74 return [expr {$pagetype == "leaf" || ($pagetype == "internal" && $is_index)}] 151 return [expr {$pagetype == "leaf" || ($pagetype == "internal" && $is_index)}]
75 } 152 }
76 proc isoverflow {pagetype is_index} { 153 proc isoverflow {pagetype is_index} {
77 return [expr {$pagetype == "overflow"}] 154 return [expr {$pagetype == "overflow"}]
78 } 155 }
79 proc isinternal {pagetype is_index} { 156 proc isinternal {pagetype is_index} {
80 return [expr {$pagetype == "internal" && $is_index==0}] 157 return [expr {$pagetype == "internal" && $is_index==0}]
81 } 158 }
82 159
83 db func isleaf isleaf 160 db func isleaf isleaf
84 db func isinternal isinternal 161 db func isinternal isinternal
85 db func isoverflow isoverflow 162 db func isoverflow isoverflow
86 163
164 set isCompressed 0
165 set compressOverhead 0
87 set sql { SELECT name, tbl_name FROM sqlite_master WHERE rootpage>0 } 166 set sql { SELECT name, tbl_name FROM sqlite_master WHERE rootpage>0 }
88 foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] { 167 foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
89 168
90 set is_index [expr {$name!=$tblname}] 169 set is_index [expr {$name!=$tblname}]
91 db eval { 170 db eval {
92 SELECT 171 SELECT
93 sum(ncell) AS nentry, 172 sum(ncell) AS nentry,
94 sum(isleaf(pagetype, $is_index) * ncell) AS leaf_entries, 173 sum(isleaf(pagetype, $is_index) * ncell) AS leaf_entries,
95 sum(payload) AS payload, 174 sum(payload) AS payload,
96 sum(isoverflow(pagetype, $is_index) * payload) AS ovfl_payload, 175 sum(isoverflow(pagetype, $is_index) * payload) AS ovfl_payload,
97 sum(path LIKE '%+000000') AS ovfl_cnt, 176 sum(path LIKE '%+000000') AS ovfl_cnt,
98 max(mx_payload) AS mx_payload, 177 max(mx_payload) AS mx_payload,
99 sum(isinternal(pagetype, $is_index)) AS int_pages, 178 sum(isinternal(pagetype, $is_index)) AS int_pages,
100 sum(isleaf(pagetype, $is_index)) AS leaf_pages, 179 sum(isleaf(pagetype, $is_index)) AS leaf_pages,
101 sum(isoverflow(pagetype, $is_index)) AS ovfl_pages, 180 sum(isoverflow(pagetype, $is_index)) AS ovfl_pages,
102 sum(isinternal(pagetype, $is_index) * unused) AS int_unused, 181 sum(isinternal(pagetype, $is_index) * unused) AS int_unused,
103 sum(isleaf(pagetype, $is_index) * unused) AS leaf_unused, 182 sum(isleaf(pagetype, $is_index) * unused) AS leaf_unused,
104 sum(isoverflow(pagetype, $is_index) * unused) AS ovfl_unused 183 sum(isoverflow(pagetype, $is_index) * unused) AS ovfl_unused,
184 sum(pgsize) AS compressed_size
105 FROM temp.dbstat WHERE name = $name 185 FROM temp.dbstat WHERE name = $name
106 } break 186 } break
107 187
188 set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}]
189 set storage [expr {$total_pages*$pageSize}]
190 if {!$isCompressed && $storage>$compressed_size} {
191 set isCompressed 1
192 set compressOverhead 14
193 }
194
108 # Column 'gap_cnt' is set to the number of non-contiguous entries in the 195 # Column 'gap_cnt' is set to the number of non-contiguous entries in the
109 # list of pages visited if the b-tree structure is traversed in a top-down 196 # list of pages visited if the b-tree structure is traversed in a top-down
110 # fashion (each node visited before its child-tree is passed). Any overflow 197 # fashion (each node visited before its child-tree is passed). Any overflow
111 # chains present are traversed from start to finish before any child-tree 198 # chains present are traversed from start to finish before any child-tree
112 # is. 199 # is.
113 # 200 #
114 set gap_cnt 0 201 set gap_cnt 0
115 set pglist [db eval { 202 set prev 0
116 SELECT pageno FROM temp.dbstat WHERE name = $name ORDER BY rowid 203 db eval {
117 }] 204 SELECT pageno, pagetype FROM temp.dbstat
118 set prev [lindex $pglist 0] 205 WHERE name=$name
119 foreach pgno [lrange $pglist 1 end] { 206 ORDER BY pageno
120 if {$pgno != $prev+1} {incr gap_cnt} 207 } {
121 set prev $pgno 208 if {$prev>0 && $pagetype=="leaf" && $pageno!=$prev+1} {
209 incr gap_cnt
210 }
211 set prev $pageno
122 } 212 }
123
124 mem eval { 213 mem eval {
125 INSERT INTO space_used VALUES( 214 INSERT INTO space_used VALUES(
126 $name, 215 $name,
127 $tblname, 216 $tblname,
128 $is_index, 217 $is_index,
129 $nentry, 218 $nentry,
130 $leaf_entries, 219 $leaf_entries,
131 $payload, 220 $payload,
132 $ovfl_payload, 221 $ovfl_payload,
133 $ovfl_cnt, 222 $ovfl_cnt,
134 $mx_payload, 223 $mx_payload,
135 $int_pages, 224 $int_pages,
136 $leaf_pages, 225 $leaf_pages,
137 $ovfl_pages, 226 $ovfl_pages,
138 $int_unused, 227 $int_unused,
139 $leaf_unused, 228 $leaf_unused,
140 $ovfl_unused, 229 $ovfl_unused,
141 $gap_cnt 230 $gap_cnt,
231 $compressed_size
142 ); 232 );
143 } 233 }
144 } 234 }
145 235
146 proc integerify {real} { 236 proc integerify {real} {
147 if {[string is double -strict $real]} { 237 if {[string is double -strict $real]} {
148 return [expr {int($real)}] 238 return [expr {wide($real)}]
149 } else { 239 } else {
150 return 0 240 return 0
151 } 241 }
152 } 242 }
153 mem function int integerify 243 mem function int integerify
154 244
155 # Quote a string for use in an SQL query. Examples: 245 # Quote a string for use in an SQL query. Examples:
156 # 246 #
157 # [quote {hello world}] == {'hello world'} 247 # [quote {hello world}] == {'hello world'}
158 # [quote {hello world's}] == {'hello world''s'} 248 # [quote {hello world's}] == {'hello world''s'}
159 # 249 #
160 proc quote {txt} { 250 proc quote {txt} {
161 regsub -all ' $txt '' q 251 return [string map {' ''} $txt]
162 return '$q' 252 }
253
254 # Output a title line
255 #
256 proc titleline {title} {
257 if {$title==""} {
258 puts [string repeat * 79]
259 } else {
260 set len [string length $title]
261 set stars [string repeat * [expr 79-$len-5]]
262 puts "*** $title $stars"
263 }
163 } 264 }
164 265
165 # Generate a single line of output in the statistics section of the 266 # Generate a single line of output in the statistics section of the
166 # report. 267 # report.
167 # 268 #
168 proc statline {title value {extra {}}} { 269 proc statline {title value {extra {}}} {
169 set len [string length $title] 270 set len [string length $title]
170 set dots [string range {......................................} $len end] 271 set dots [string repeat . [expr 50-$len]]
171 set len [string length $value] 272 set len [string length $value]
172 set sp2 [string range { } $len end] 273 set sp2 [string range { } $len end]
173 if {$extra ne ""} { 274 if {$extra ne ""} {
174 set extra " $extra" 275 set extra " $extra"
175 } 276 }
176 puts "$title$dots $value$sp2$extra" 277 puts "$title$dots $value$sp2$extra"
177 } 278 }
178 279
179 # Generate a formatted percentage value for $num/$denom 280 # Generate a formatted percentage value for $num/$denom
180 # 281 #
(...skipping 11 matching lines...) Expand all
192 } 293 }
193 294
194 proc divide {num denom} { 295 proc divide {num denom} {
195 if {$denom==0} {return 0.0} 296 if {$denom==0} {return 0.0}
196 return [format %.2f [expr double($num)/double($denom)]] 297 return [format %.2f [expr double($num)/double($denom)]]
197 } 298 }
198 299
199 # Generate a subreport that covers some subset of the database. 300 # Generate a subreport that covers some subset of the database.
200 # the $where clause determines which subset to analyze. 301 # the $where clause determines which subset to analyze.
201 # 302 #
202 proc subreport {title where} { 303 proc subreport {title where showFrag} {
203 global pageSize file_pgcnt 304 global pageSize file_pgcnt compressOverhead
204 305
205 # Query the in-memory database for the sum of various statistics 306 # Query the in-memory database for the sum of various statistics
206 # for the subset of tables/indices identified by the WHERE clause in 307 # for the subset of tables/indices identified by the WHERE clause in
207 # $where. Note that even if the WHERE clause matches no rows, the 308 # $where. Note that even if the WHERE clause matches no rows, the
208 # following query returns exactly one row (because it is an aggregate). 309 # following query returns exactly one row (because it is an aggregate).
209 # 310 #
210 # The results of the query are stored directly by SQLite into local 311 # The results of the query are stored directly by SQLite into local
211 # variables (i.e. $nentry, $nleaf etc.). 312 # variables (i.e. $nentry, $nleaf etc.).
212 # 313 #
213 mem eval " 314 mem eval "
214 SELECT 315 SELECT
215 int(sum(nentry)) AS nentry, 316 int(sum(nentry)) AS nentry,
216 int(sum(leaf_entries)) AS nleaf, 317 int(sum(leaf_entries)) AS nleaf,
217 int(sum(payload)) AS payload, 318 int(sum(payload)) AS payload,
218 int(sum(ovfl_payload)) AS ovfl_payload, 319 int(sum(ovfl_payload)) AS ovfl_payload,
219 max(mx_payload) AS mx_payload, 320 max(mx_payload) AS mx_payload,
220 int(sum(ovfl_cnt)) as ovfl_cnt, 321 int(sum(ovfl_cnt)) as ovfl_cnt,
221 int(sum(leaf_pages)) AS leaf_pages, 322 int(sum(leaf_pages)) AS leaf_pages,
222 int(sum(int_pages)) AS int_pages, 323 int(sum(int_pages)) AS int_pages,
223 int(sum(ovfl_pages)) AS ovfl_pages, 324 int(sum(ovfl_pages)) AS ovfl_pages,
224 int(sum(leaf_unused)) AS leaf_unused, 325 int(sum(leaf_unused)) AS leaf_unused,
225 int(sum(int_unused)) AS int_unused, 326 int(sum(int_unused)) AS int_unused,
226 int(sum(ovfl_unused)) AS ovfl_unused, 327 int(sum(ovfl_unused)) AS ovfl_unused,
227 int(sum(gap_cnt)) AS gap_cnt 328 int(sum(gap_cnt)) AS gap_cnt,
329 int(sum(compressed_size)) AS compressed_size
228 FROM space_used WHERE $where" {} {} 330 FROM space_used WHERE $where" {} {}
229 331
230 # Output the sub-report title, nicely decorated with * characters. 332 # Output the sub-report title, nicely decorated with * characters.
231 # 333 #
232 puts "" 334 puts ""
233 set len [string length $title] 335 titleline $title
234 set stars [string repeat * [expr 65-$len]]
235 puts "*** $title $stars"
236 puts "" 336 puts ""
237 337
238 # Calculate statistics and store the results in TCL variables, as follows: 338 # Calculate statistics and store the results in TCL variables, as follows:
239 # 339 #
240 # total_pages: Database pages consumed. 340 # total_pages: Database pages consumed.
241 # total_pages_percent: Pages consumed as a percentage of the file. 341 # total_pages_percent: Pages consumed as a percentage of the file.
242 # storage: Bytes consumed. 342 # storage: Bytes consumed.
243 # payload_percent: Payload bytes used as a percentage of $storage. 343 # payload_percent: Payload bytes used as a percentage of $storage.
244 # total_unused: Unused bytes on pages. 344 # total_unused: Unused bytes on pages.
245 # avg_payload: Average payload per btree entry. 345 # avg_payload: Average payload per btree entry.
(...skipping 21 matching lines...) Expand all
267 "] 367 "]
268 set avg_fanout [format %.2f $avg_fanout] 368 set avg_fanout [format %.2f $avg_fanout]
269 } 369 }
270 set ovfl_cnt_percent [percent $ovfl_cnt $nleaf {of all entries}] 370 set ovfl_cnt_percent [percent $ovfl_cnt $nleaf {of all entries}]
271 371
272 # Print out the sub-report statistics. 372 # Print out the sub-report statistics.
273 # 373 #
274 statline {Percentage of total database} $total_pages_percent 374 statline {Percentage of total database} $total_pages_percent
275 statline {Number of entries} $nleaf 375 statline {Number of entries} $nleaf
276 statline {Bytes of storage consumed} $storage 376 statline {Bytes of storage consumed} $storage
377 if {$compressed_size!=$storage} {
378 set compressed_size [expr {$compressed_size+$compressOverhead*$total_pages}]
379 set pct [expr {$compressed_size*100.0/$storage}]
380 set pct [format {%5.1f%%} $pct]
381 statline {Bytes used after compression} $compressed_size $pct
382 }
277 statline {Bytes of payload} $payload $payload_percent 383 statline {Bytes of payload} $payload $payload_percent
278 statline {Average payload per entry} $avg_payload 384 statline {Average payload per entry} $avg_payload
279 statline {Average unused bytes per entry} $avg_unused 385 statline {Average unused bytes per entry} $avg_unused
280 if {[info exists avg_fanout]} { 386 if {[info exists avg_fanout]} {
281 statline {Average fanout} $avg_fanout 387 statline {Average fanout} $avg_fanout
282 } 388 }
283 if {$total_pages>1} { 389 if {$showFrag && $total_pages>1} {
284 set fragmentation [percent $gap_cnt [expr {$total_pages-1}] {fragmentation}] 390 set fragmentation [percent $gap_cnt [expr {$total_pages-1}]]
285 statline {Fragmentation} $fragmentation 391 statline {Non-sequential pages} $gap_cnt $fragmentation
286 } 392 }
287 statline {Maximum payload per entry} $mx_payload 393 statline {Maximum payload per entry} $mx_payload
288 statline {Entries that use overflow} $ovfl_cnt $ovfl_cnt_percent 394 statline {Entries that use overflow} $ovfl_cnt $ovfl_cnt_percent
289 if {$int_pages>0} { 395 if {$int_pages>0} {
290 statline {Index pages used} $int_pages 396 statline {Index pages used} $int_pages
291 } 397 }
292 statline {Primary pages used} $leaf_pages 398 statline {Primary pages used} $leaf_pages
293 statline {Overflow pages used} $ovfl_pages 399 statline {Overflow pages used} $ovfl_pages
294 statline {Total pages used} $total_pages 400 statline {Total pages used} $total_pages
295 if {$int_unused>0} { 401 if {$int_unused>0} {
296 set int_unused_percent \ 402 set int_unused_percent [
297 [percent $int_unused [expr {$int_pages*$pageSize}] {of index space}] 403 percent $int_unused [expr {$int_pages*$pageSize}] {of index space}]
298 statline "Unused bytes on index pages" $int_unused $int_unused_percent 404 statline "Unused bytes on index pages" $int_unused $int_unused_percent
299 } 405 }
300 statline "Unused bytes on primary pages" $leaf_unused \ 406 statline "Unused bytes on primary pages" $leaf_unused [
301 [percent $leaf_unused [expr {$leaf_pages*$pageSize}] {of primary space}] 407 percent $leaf_unused [expr {$leaf_pages*$pageSize}] {of primary space}]
302 statline "Unused bytes on overflow pages" $ovfl_unused \ 408 statline "Unused bytes on overflow pages" $ovfl_unused [
303 [percent $ovfl_unused [expr {$ovfl_pages*$pageSize}] {of overflow space}] 409 percent $ovfl_unused [expr {$ovfl_pages*$pageSize}] {of overflow space}]
304 statline "Unused bytes on all pages" $total_unused \ 410 statline "Unused bytes on all pages" $total_unused [
305 [percent $total_unused $storage {of all space}] 411 percent $total_unused $storage {of all space}]
306 return 1 412 return 1
307 } 413 }
308 414
309 # Calculate the overhead in pages caused by auto-vacuum. 415 # Calculate the overhead in pages caused by auto-vacuum.
310 # 416 #
311 # This procedure calculates and returns the number of pages used by the 417 # This procedure calculates and returns the number of pages used by the
312 # auto-vacuum 'pointer-map'. If the database does not support auto-vacuum, 418 # auto-vacuum 'pointer-map'. If the database does not support auto-vacuum,
313 # then 0 is returned. The two arguments are the size of the database file in 419 # then 0 is returned. The two arguments are the size of the database file in
314 # pages and the page size used by the database (in bytes). 420 # pages and the page size used by the database (in bytes).
315 proc autovacuum_overhead {filePages pageSize} { 421 proc autovacuum_overhead {filePages pageSize} {
316 422
317 # Set $autovacuum to non-zero for databases that support auto-vacuum. 423 # Set $autovacuum to non-zero for databases that support auto-vacuum.
318 set autovacuum [db one {PRAGMA auto_vacuum}] 424 set autovacuum [db one {PRAGMA auto_vacuum}]
319 425
320 # If the database is not an auto-vacuum database or the file consists 426 # If the database is not an auto-vacuum database or the file consists
321 # of one page only then there is no overhead for auto-vacuum. Return zero. 427 # of one page only then there is no overhead for auto-vacuum. Return zero.
322 if {0==$autovacuum || $filePages==1} { 428 if {0==$autovacuum || $filePages==1} {
323 return 0 429 return 0
324 } 430 }
325 431
326 # The number of entries on each pointer map page. The layout of the 432 # The number of entries on each pointer map page. The layout of the
327 # database file is one pointer-map page, followed by $ptrsPerPage other 433 # database file is one pointer-map page, followed by $ptrsPerPage other
328 # pages, followed by a pointer-map page etc. The first pointer-map page 434 # pages, followed by a pointer-map page etc. The first pointer-map page
329 # is the second page of the file overall. 435 # is the second page of the file overall.
330 set ptrsPerPage [expr double($pageSize/5)] 436 set ptrsPerPage [expr double($pageSize/5)]
331 437
332 # Return the number of pointer map pages in the database. 438 # Return the number of pointer map pages in the database.
333 return [expr int(ceil( ($filePages-1.0)/($ptrsPerPage+1.0) ))] 439 return [expr wide(ceil( ($filePages-1.0)/($ptrsPerPage+1.0) ))]
334 } 440 }
335 441
336 442
337 # Calculate the summary statistics for the database and store the results 443 # Calculate the summary statistics for the database and store the results
338 # in TCL variables. They are output below. Variables are as follows: 444 # in TCL variables. They are output below. Variables are as follows:
339 # 445 #
340 # pageSize: Size of each page in bytes. 446 # pageSize: Size of each page in bytes.
341 # file_bytes: File size in bytes. 447 # file_bytes: File size in bytes.
342 # file_pgcnt: Number of pages in the file. 448 # file_pgcnt: Number of pages in the file.
343 # file_pgcnt2: Number of pages in the file (calculated). 449 # file_pgcnt2: Number of pages in the file (calculated).
344 # av_pgcnt: Pages consumed by the auto-vacuum pointer-map. 450 # av_pgcnt: Pages consumed by the auto-vacuum pointer-map.
345 # av_percent: Percentage of the file consumed by auto-vacuum pointer-map. 451 # av_percent: Percentage of the file consumed by auto-vacuum pointer-map.
346 # inuse_pgcnt: Data pages in the file. 452 # inuse_pgcnt: Data pages in the file.
347 # inuse_percent: Percentage of pages used to store data. 453 # inuse_percent: Percentage of pages used to store data.
348 # free_pgcnt: Free pages calculated as (<total pages> - <in-use pages>) 454 # free_pgcnt: Free pages calculated as (<total pages> - <in-use pages>)
349 # free_pgcnt2: Free pages in the file according to the file header. 455 # free_pgcnt2: Free pages in the file according to the file header.
350 # free_percent: Percentage of file consumed by free pages (calculated). 456 # free_percent: Percentage of file consumed by free pages (calculated).
351 # free_percent2: Percentage of file consumed by free pages (header). 457 # free_percent2: Percentage of file consumed by free pages (header).
352 # ntable: Number of tables in the db. 458 # ntable: Number of tables in the db.
353 # nindex: Number of indices in the db. 459 # nindex: Number of indices in the db.
354 # nautoindex: Number of indices created automatically. 460 # nautoindex: Number of indices created automatically.
355 # nmanindex: Number of indices created manually. 461 # nmanindex: Number of indices created manually.
356 # user_payload: Number of bytes of payload in table btrees 462 # user_payload: Number of bytes of payload in table btrees
357 # (not including sqlite_master) 463 # (not including sqlite_master)
358 # user_percent: $user_payload as a percentage of total file size. 464 # user_percent: $user_payload as a percentage of total file size.
359 465
360 set file_bytes [file size $file_to_analyze] 466 ### The following, setting $file_bytes based on the actual size of the file
361 set file_pgcnt [expr {$file_bytes/$pageSize}] 467 ### on disk, causes this tool to choke on zipvfs databases. So set it based
468 ### on the return of [PRAGMA page_count] instead.
469 if 0 {
470 set file_bytes [file size $file_to_analyze]
471 set file_pgcnt [expr {$file_bytes/$pageSize}]
472 }
473 set file_pgcnt [db one {PRAGMA page_count}]
474 set file_bytes [expr {$file_pgcnt * $pageSize}]
362 475
363 set av_pgcnt [autovacuum_overhead $file_pgcnt $pageSize] 476 set av_pgcnt [autovacuum_overhead $file_pgcnt $pageSize]
364 set av_percent [percent $av_pgcnt $file_pgcnt] 477 set av_percent [percent $av_pgcnt $file_pgcnt]
365 478
366 set sql {SELECT sum(leaf_pages+int_pages+ovfl_pages) FROM space_used} 479 set sql {SELECT sum(leaf_pages+int_pages+ovfl_pages) FROM space_used}
367 set inuse_pgcnt [expr int([mem eval $sql])] 480 set inuse_pgcnt [expr wide([mem eval $sql])]
368 set inuse_percent [percent $inuse_pgcnt $file_pgcnt] 481 set inuse_percent [percent $inuse_pgcnt $file_pgcnt]
369 482
370 set free_pgcnt [expr $file_pgcnt-$inuse_pgcnt-$av_pgcnt] 483 set free_pgcnt [expr {$file_pgcnt-$inuse_pgcnt-$av_pgcnt}]
371 set free_percent [percent $free_pgcnt $file_pgcnt] 484 set free_percent [percent $free_pgcnt $file_pgcnt]
372 set free_pgcnt2 [db one {PRAGMA freelist_count}] 485 set free_pgcnt2 [db one {PRAGMA freelist_count}]
373 set free_percent2 [percent $free_pgcnt2 $file_pgcnt] 486 set free_percent2 [percent $free_pgcnt2 $file_pgcnt]
374 487
375 set file_pgcnt2 [expr {$inuse_pgcnt+$free_pgcnt2+$av_pgcnt}] 488 set file_pgcnt2 [expr {$inuse_pgcnt+$free_pgcnt2+$av_pgcnt}]
376 489
377 set ntable [db eval {SELECT count(*)+1 FROM sqlite_master WHERE type='table'}] 490 set ntable [db eval {SELECT count(*)+1 FROM sqlite_master WHERE type='table'}]
378 set nindex [db eval {SELECT count(*) FROM sqlite_master WHERE type='index'}] 491 set nindex [db eval {SELECT count(*) FROM sqlite_master WHERE type='index'}]
379 set sql {SELECT count(*) FROM sqlite_master WHERE name LIKE 'sqlite_autoindex%'} 492 set sql {SELECT count(*) FROM sqlite_master WHERE name LIKE 'sqlite_autoindex%'}
380 set nautoindex [db eval $sql] 493 set nautoindex [db eval $sql]
381 set nmanindex [expr {$nindex-$nautoindex}] 494 set nmanindex [expr {$nindex-$nautoindex}]
382 495
383 # set total_payload [mem eval "SELECT sum(payload) FROM space_used"] 496 # set total_payload [mem eval "SELECT sum(payload) FROM space_used"]
384 set user_payload [mem one {SELECT int(sum(payload)) FROM space_used 497 set user_payload [mem one {SELECT int(sum(payload)) FROM space_used
385 WHERE NOT is_index AND name NOT LIKE 'sqlite_master'}] 498 WHERE NOT is_index AND name NOT LIKE 'sqlite_master'}]
386 set user_percent [percent $user_payload $file_bytes] 499 set user_percent [percent $user_payload $file_bytes]
387 500
388 # Output the summary statistics calculated above. 501 # Output the summary statistics calculated above.
389 # 502 #
390 puts "/** Disk-Space Utilization Report For $file_to_analyze" 503 puts "/** Disk-Space Utilization Report For $root_filename"
391 catch {
392 puts "*** As of [clock format [clock seconds] -format {%Y-%b-%d %H:%M:%S}]"
393 }
394 puts "" 504 puts ""
395 statline {Page size in bytes} $pageSize 505 statline {Page size in bytes} $pageSize
396 statline {Pages in the whole file (measured)} $file_pgcnt 506 statline {Pages in the whole file (measured)} $file_pgcnt
397 statline {Pages in the whole file (calculated)} $file_pgcnt2 507 statline {Pages in the whole file (calculated)} $file_pgcnt2
398 statline {Pages that store data} $inuse_pgcnt $inuse_percent 508 statline {Pages that store data} $inuse_pgcnt $inuse_percent
399 statline {Pages on the freelist (per header)} $free_pgcnt2 $free_percent2 509 statline {Pages on the freelist (per header)} $free_pgcnt2 $free_percent2
400 statline {Pages on the freelist (calculated)} $free_pgcnt $free_percent 510 statline {Pages on the freelist (calculated)} $free_pgcnt $free_percent
401 statline {Pages of auto-vacuum overhead} $av_pgcnt $av_percent 511 statline {Pages of auto-vacuum overhead} $av_pgcnt $av_percent
402 statline {Number of tables in the database} $ntable 512 statline {Number of tables in the database} $ntable
403 statline {Number of indices} $nindex 513 statline {Number of indices} $nindex
404 statline {Number of named indices} $nmanindex 514 statline {Number of defined indices} $nmanindex
405 statline {Automatically generated indices} $nautoindex 515 statline {Number of implied indices} $nautoindex
406 statline {Size of the file in bytes} $file_bytes 516 if {$isCompressed} {
517 statline {Size of uncompressed content in bytes} $file_bytes
518 set efficiency [percent $true_file_size $file_bytes]
519 statline {Size of compressed file on disk} $true_file_size $efficiency
520 } else {
521 statline {Size of the file in bytes} $file_bytes
522 }
407 statline {Bytes of user payload stored} $user_payload $user_percent 523 statline {Bytes of user payload stored} $user_payload $user_percent
408 524
409 # Output table rankings 525 # Output table rankings
410 # 526 #
411 puts "" 527 puts ""
412 puts "*** Page counts for all tables with their indices ********************" 528 titleline "Page counts for all tables with their indices"
413 puts "" 529 puts ""
414 mem eval {SELECT tblname, count(*) AS cnt, 530 mem eval {SELECT tblname, count(*) AS cnt,
415 int(sum(int_pages+leaf_pages+ovfl_pages)) AS size 531 int(sum(int_pages+leaf_pages+ovfl_pages)) AS size
416 FROM space_used GROUP BY tblname ORDER BY size+0 DESC, tblname} {} { 532 FROM space_used GROUP BY tblname ORDER BY size+0 DESC, tblname} {} {
417 statline [string toupper $tblname] $size [percent $size $file_pgcnt] 533 statline [string toupper $tblname] $size [percent $size $file_pgcnt]
418 } 534 }
535 puts ""
536 titleline "Page counts for all tables and indices separately"
537 puts ""
538 mem eval {
539 SELECT
540 upper(name) AS nm,
541 int(int_pages+leaf_pages+ovfl_pages) AS size
542 FROM space_used
543 ORDER BY size+0 DESC, name} {} {
544 statline $nm $size [percent $size $file_pgcnt]
545 }
546 if {$isCompressed} {
547 puts ""
548 titleline "Bytes of disk space used after compression"
549 puts ""
550 set csum 0
551 mem eval {SELECT tblname,
552 int(sum(compressed_size)) +
553 $compressOverhead*sum(int_pages+leaf_pages+ovfl_pages)
554 AS csize
555 FROM space_used GROUP BY tblname ORDER BY csize+0 DESC, tblname} {} {
556 incr csum $csize
557 statline [string toupper $tblname] $csize [percent $csize $true_file_size]
558 }
559 set overhead [expr {$true_file_size - $csum}]
560 if {$overhead>0} {
561 statline {Header and free space} $overhead [percent $overhead $true_file_siz e]
562 }
563 }
419 564
420 # Output subreports 565 # Output subreports
421 # 566 #
422 if {$nindex>0} { 567 if {$nindex>0} {
423 subreport {All tables and indices} 1 568 subreport {All tables and indices} 1 0
424 } 569 }
425 subreport {All tables} {NOT is_index} 570 subreport {All tables} {NOT is_index} 0
426 if {$nindex>0} { 571 if {$nindex>0} {
427 subreport {All indices} {is_index} 572 subreport {All indices} {is_index} 0
428 } 573 }
429 foreach tbl [mem eval {SELECT name FROM space_used WHERE NOT is_index 574 foreach tbl [mem eval {SELECT DISTINCT tblname name FROM space_used
430 ORDER BY name}] { 575 ORDER BY name}] {
431 regsub ' $tbl '' qn 576 set qn [quote $tbl]
432 set name [string toupper $tbl] 577 set name [string toupper $tbl]
433 set n [mem eval "SELECT count(*) FROM space_used WHERE tblname='$qn'"] 578 set n [mem eval {SELECT count(*) FROM space_used WHERE tblname=$tbl}]
434 if {$n>1} { 579 if {$n>1} {
435 subreport "Table $name and all its indices" "tblname='$qn'" 580 set idxlist [mem eval "SELECT name FROM space_used
436 subreport "Table $name w/o any indices" "name='$qn'" 581 WHERE tblname='$qn' AND is_index
437 subreport "Indices of table $name" "tblname='$qn' AND is_index" 582 ORDER BY 1"]
583 subreport "Table $name and all its indices" "tblname='$qn'" 0
584 subreport "Table $name w/o any indices" "name='$qn'" 1
585 if {[llength $idxlist]>1} {
586 subreport "Indices of table $name" "tblname='$qn' AND is_index" 0
587 }
588 foreach idx $idxlist {
589 set qidx [quote $idx]
590 subreport "Index [string toupper $idx] of table $name" "name='$qidx'" 1
591 }
438 } else { 592 } else {
439 subreport "Table $name" "name='$qn'" 593 subreport "Table $name" "name='$qn'" 1
440 } 594 }
441 } 595 }
442 596
443 # Output instructions on what the numbers above mean. 597 # Output instructions on what the numbers above mean.
444 # 598 #
599 puts ""
600 titleline Definitions
445 puts { 601 puts {
446 *** Definitions ******************************************************
447
448 Page size in bytes 602 Page size in bytes
449 603
450 The number of bytes in a single page of the database file. 604 The number of bytes in a single page of the database file.
451 Usually 1024. 605 Usually 1024.
452 606
453 Number of pages in the whole file 607 Number of pages in the whole file
454 } 608 }
455 puts \ 609 puts " The number of $pageSize-byte pages that go into forming the complete
456 " The number of $pageSize-byte pages that go into forming the complete
457 database" 610 database"
458 puts \ 611 puts {
459 {
460 Pages that store data 612 Pages that store data
461 613
462 The number of pages that store data, either as primary B*Tree pages or 614 The number of pages that store data, either as primary B*Tree pages or
463 as overflow pages. The number at the right is the data pages divided by 615 as overflow pages. The number at the right is the data pages divided by
464 the total number of pages in the file. 616 the total number of pages in the file.
465 617
466 Pages on the freelist 618 Pages on the freelist
467 619
468 The number of pages that are not currently in use but are reserved for 620 The number of pages that are not currently in use but are reserved for
469 future use. The percentage at the right is the number of freelist pages 621 future use. The percentage at the right is the number of freelist pages
470 divided by the total number of pages in the file. 622 divided by the total number of pages in the file.
471 623
472 Pages of auto-vacuum overhead 624 Pages of auto-vacuum overhead
473 625
474 The number of pages that store data used by the database to facilitate 626 The number of pages that store data used by the database to facilitate
475 auto-vacuum. This is zero for databases that do not support auto-vacuum. 627 auto-vacuum. This is zero for databases that do not support auto-vacuum.
476 628
477 Number of tables in the database 629 Number of tables in the database
478 630
479 The number of tables in the database, including the SQLITE_MASTER table 631 The number of tables in the database, including the SQLITE_MASTER table
480 used to store schema information. 632 used to store schema information.
481 633
482 Number of indices 634 Number of indices
483 635
484 The total number of indices in the database. 636 The total number of indices in the database.
485 637
486 Number of named indices 638 Number of defined indices
487 639
488 The number of indices created using an explicit CREATE INDEX statement. 640 The number of indices created using an explicit CREATE INDEX statement.
489 641
490 Automatically generated indices 642 Number of implied indices
491 643
492 The number of indices used to implement PRIMARY KEY or UNIQUE constraints 644 The number of indices used to implement PRIMARY KEY or UNIQUE constraints
493 on tables. 645 on tables.
494 646
495 Size of the file in bytes 647 Size of the file in bytes
496 648
497 The total amount of disk space used by the entire database files. 649 The total amount of disk space used by the entire database files.
498 650
499 Bytes of user payload stored 651 Bytes of user payload stored
500 652
(...skipping 28 matching lines...) Expand all
529 681
530 The average amount of payload on each entry. This is just the bytes of 682 The average amount of payload on each entry. This is just the bytes of
531 payload divided by the number of entries. 683 payload divided by the number of entries.
532 684
533 Average unused bytes per entry 685 Average unused bytes per entry
534 686
535 The average amount of free space remaining on all pages under this 687 The average amount of free space remaining on all pages under this
536 category on a per-entry basis. This is the number of unused bytes on 688 category on a per-entry basis. This is the number of unused bytes on
537 all pages divided by the number of entries. 689 all pages divided by the number of entries.
538 690
539 Fragmentation 691 Non-sequential pages
540 692
541 The percentage of pages in the table or index that are not 693 The number of pages in the table or index that are out of sequence.
542 consecutive in the disk file. Many filesystems are optimized 694 Many filesystems are optimized for sequential file access so a small
543 for sequential file access so smaller fragmentation numbers 695 number of non-sequential pages might result in faster queries,
544 sometimes result in faster queries, especially for larger 696 especially for larger database files that do not fit in the disk cache.
545 database files that do not fit in the disk cache. 697 Note that after running VACUUM, the root page of each table or index is
698 at the beginning of the database file and all other pages are in a
699 separate part of the database file, resulting in a single non-
700 sequential page.
546 701
547 Maximum payload per entry 702 Maximum payload per entry
548 703
549 The largest payload size of any entry. 704 The largest payload size of any entry.
550 705
551 Entries that use overflow 706 Entries that use overflow
552 707
553 The number of entries that user one or more overflow pages. 708 The number of entries that user one or more overflow pages.
554 709
555 Total pages used 710 Total pages used
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
591 Unused bytes on all pages 746 Unused bytes on all pages
592 747
593 The total number of bytes of unused space on all primary and overflow 748 The total number of bytes of unused space on all primary and overflow
594 pages. The percentage at the right is the number of unused bytes 749 pages. The percentage at the right is the number of unused bytes
595 divided by the total number of bytes. 750 divided by the total number of bytes.
596 } 751 }
597 752
598 # Output a dump of the in-memory database. This can be used for more 753 # Output a dump of the in-memory database. This can be used for more
599 # complex offline analysis. 754 # complex offline analysis.
600 # 755 #
601 puts "**********************************************************************" 756 titleline {}
602 puts "The entire text of this report can be sourced into any SQL database" 757 puts "The entire text of this report can be sourced into any SQL database"
603 puts "engine for further analysis. All of the text above is an SQL comment." 758 puts "engine for further analysis. All of the text above is an SQL comment."
604 puts "The data used to generate this report follows:" 759 puts "The data used to generate this report follows:"
605 puts "*/" 760 puts "*/"
606 puts "BEGIN;" 761 puts "BEGIN;"
607 puts $tabledef 762 puts $tabledef
608 unset -nocomplain x 763 unset -nocomplain x
609 mem eval {SELECT * FROM space_used} x { 764 mem eval {SELECT * FROM space_used} x {
610 puts -nonewline "INSERT INTO space_used VALUES" 765 puts -nonewline "INSERT INTO space_used VALUES"
611 set sep ( 766 set sep (
612 foreach col $x(*) { 767 foreach col $x(*) {
613 set v $x($col) 768 set v $x($col)
614 if {$v=="" || ![string is double $v]} {set v [quote $v]} 769 if {$v=="" || ![string is double $v]} {set v '[quote $v]'}
615 puts -nonewline $sep$v 770 puts -nonewline $sep$v
616 set sep , 771 set sep ,
617 } 772 }
618 puts ");" 773 puts ");"
619 } 774 }
620 puts "COMMIT;" 775 puts "COMMIT;"
621 776
622 } err]} { 777 } err]} {
623 puts "ERROR: $err" 778 puts "ERROR: $err"
624 puts $errorInfo 779 puts $errorInfo
625 exit 1 780 exit 1
626 } 781 }
OLDNEW
« no previous file with comments | « third_party/sqlite/src/tool/showwal.c ('k') | third_party/sqlite/src/tool/stack_usage.tcl » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698