OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |