15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Run this TCL script using "testfixture" in order get a report that shows
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# how much disk space is used by a particular data to actually store data
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# versus how much space is unused.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if {[catch {
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Get the name of the database to analyze
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#set argv $argv0
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if {[llength $argv]!=1} {
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  puts stderr "Usage: $argv0 database-name"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit 1
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set file_to_analyze [lindex $argv 0]
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if {![file exists $file_to_analyze]} {
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  puts stderr "No such file: $file_to_analyze"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit 1
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if {![file readable $file_to_analyze]} {
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  puts stderr "File is not readable: $file_to_analyze"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit 1
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if {[file size $file_to_analyze]<512} {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  puts stderr "Empty or malformed database: $file_to_analyze"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit 1
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Open the database
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sqlite3 db [lindex $argv 0]
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)register_dbstat_vtab db
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set pageSize [db one {PRAGMA page_size}]
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#set DB [btree_open [lindex $argv 0] 1000 0]
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# In-memory database for collecting statistics. This script loops through
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the tables and indices in the database being analyzed, adding a row for each
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# to an in-memory database (for which the schema is shown below). It then
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# queries the in-memory db to produce the space-analysis report.
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)sqlite3 mem :memory:
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set tabledef\
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){CREATE TABLE space_used(
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   name clob,        -- Name of a table or index in the database file
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   tblname clob,     -- Name of associated table
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   is_index boolean, -- TRUE if it is an index, false for a table
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   nentry int,       -- Number of entries in the BTree
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   leaf_entries int, -- Number of leaf entries
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   payload int,      -- Total amount of data stored in this table or index
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   ovfl_payload int, -- Total amount of data stored on overflow pages
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   ovfl_cnt int,     -- Number of entries that use overflow
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   mx_payload int,   -- Maximum payload size
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   int_pages int,    -- Number of interior pages used
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   leaf_pages int,   -- Number of leaf pages used
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   ovfl_pages int,   -- Number of overflow pages used
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   int_unused int,   -- Number of unused bytes on interior pages
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   leaf_unused int,  -- Number of unused bytes on primary pages
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   ovfl_unused int,  -- Number of unused bytes on overflow pages
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   gap_cnt int       -- Number of gaps in the page layout
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles));}
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)mem eval $tabledef
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Create a temporary "dbstat" virtual table.
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)db eval {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CREATE VIRTUAL TABLE temp.stat USING dbstat;
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CREATE TEMP TABLE dbstat AS SELECT * FROM temp.stat ORDER BY name, path;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DROP TABLE temp.stat;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)proc isleaf {pagetype is_index} {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return [expr {$pagetype == "leaf" || ($pagetype == "internal" && $is_index)}]
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)proc isoverflow {pagetype is_index} {
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return [expr {$pagetype == "overflow"}]
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)proc isinternal {pagetype is_index} {
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return [expr {$pagetype == "internal" && $is_index==0}]
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)db func isleaf isleaf
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)db func isinternal isinternal
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)db func isoverflow isoverflow
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set sql { SELECT name, tbl_name FROM sqlite_master WHERE rootpage>0 }
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set is_index [expr {$name!=$tblname}]
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  db eval {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SELECT
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sum(ncell) AS nentry,
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sum(isleaf(pagetype, $is_index) * ncell) AS leaf_entries,
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sum(payload) AS payload,
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sum(isoverflow(pagetype, $is_index) * payload) AS ovfl_payload,
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sum(path LIKE '%+000000') AS ovfl_cnt,
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max(mx_payload) AS mx_payload,
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sum(isinternal(pagetype, $is_index)) AS int_pages,
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sum(isleaf(pagetype, $is_index)) AS leaf_pages,
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sum(isoverflow(pagetype, $is_index)) AS ovfl_pages,
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sum(isinternal(pagetype, $is_index) * unused) AS int_unused,
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sum(isleaf(pagetype, $is_index) * unused) AS leaf_unused,
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      sum(isoverflow(pagetype, $is_index) * unused) AS ovfl_unused
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FROM temp.dbstat WHERE name = $name
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } break
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Column 'gap_cnt' is set to the number of non-contiguous entries in the
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # list of pages visited if the b-tree structure is traversed in a top-down
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # fashion (each node visited before its child-tree is passed). Any overflow
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # chains present are traversed from start to finish before any child-tree
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # is.
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set gap_cnt 0
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set pglist [db eval {
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SELECT pageno FROM temp.dbstat WHERE name = $name ORDER BY rowid
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }]
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set prev [lindex $pglist 0]
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach pgno [lrange $pglist 1 end] {
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if {$pgno != $prev+1} {incr gap_cnt}
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set prev $pgno
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mem eval {
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    INSERT INTO space_used VALUES(
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $name,
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $tblname,
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $is_index,
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $nentry,
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $leaf_entries,
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $payload,
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $ovfl_payload,
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $ovfl_cnt,
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $mx_payload,
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $int_pages,
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $leaf_pages,
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $ovfl_pages,
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $int_unused,
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $leaf_unused,
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $ovfl_unused,
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      $gap_cnt
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    );
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)proc integerify {real} {
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if {[string is double -strict $real]} {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return [expr {int($real)}]
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)mem function int integerify
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Quote a string for use in an SQL query. Examples:
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# [quote {hello world}]   == {'hello world'}
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# [quote {hello world's}] == {'hello world''s'}
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)proc quote {txt} {
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  regsub -all ' $txt '' q
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return '$q'
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Generate a single line of output in the statistics section of the
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# report.
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)proc statline {title value {extra {}}} {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set len [string length $title]
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set dots [string range {......................................} $len end]
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set len [string length $value]
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set sp2 [string range {          } $len end]
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if {$extra ne ""} {
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set extra " $extra"
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  puts "$title$dots $value$sp2$extra"
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Generate a formatted percentage value for $num/$denom
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)proc percent {num denom {of {}}} {
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if {$denom==0.0} {return ""}
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set v [expr {$num*100.0/$denom}]
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set of {}
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if {$v==100.0 || $v<0.001 || ($v>1.0 && $v<99.0)} {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return [format {%5.1f%% %s} $v $of]
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } elseif {$v<0.1 || $v>99.9} {
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return [format {%7.3f%% %s} $v $of]
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return [format {%6.2f%% %s} $v $of]
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)proc divide {num denom} {
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if {$denom==0} {return 0.0}
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return [format %.2f [expr double($num)/double($denom)]]
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Generate a subreport that covers some subset of the database.
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the $where clause determines which subset to analyze.
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)proc subreport {title where} {
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  global pageSize file_pgcnt
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Query the in-memory database for the sum of various statistics
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # for the subset of tables/indices identified by the WHERE clause in
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # $where. Note that even if the WHERE clause matches no rows, the
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # following query returns exactly one row (because it is an aggregate).
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The results of the query are stored directly by SQLite into local
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # variables (i.e. $nentry, $nleaf etc.).
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mem eval "
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SELECT
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int(sum(nentry)) AS nentry,
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int(sum(leaf_entries)) AS nleaf,
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int(sum(payload)) AS payload,
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int(sum(ovfl_payload)) AS ovfl_payload,
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max(mx_payload) AS mx_payload,
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int(sum(ovfl_cnt)) as ovfl_cnt,
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int(sum(leaf_pages)) AS leaf_pages,
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int(sum(int_pages)) AS int_pages,
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int(sum(ovfl_pages)) AS ovfl_pages,
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int(sum(leaf_unused)) AS leaf_unused,
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int(sum(int_unused)) AS int_unused,
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int(sum(ovfl_unused)) AS ovfl_unused,
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int(sum(gap_cnt)) AS gap_cnt
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FROM space_used WHERE $where" {} {}
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Output the sub-report title, nicely decorated with * characters.
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  puts ""
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set len [string length $title]
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set stars [string repeat * [expr 65-$len]]
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  puts "*** $title $stars"
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  puts ""
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Calculate statistics and store the results in TCL variables, as follows:
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # total_pages: Database pages consumed.
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # total_pages_percent: Pages consumed as a percentage of the file.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # storage: Bytes consumed.
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # payload_percent: Payload bytes used as a percentage of $storage.
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # total_unused: Unused bytes on pages.
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # avg_payload: Average payload per btree entry.
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # avg_fanout: Average fanout for internal pages.
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # avg_unused: Average unused bytes per btree entry.
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # ovfl_cnt_percent: Percentage of btree entries that use overflow pages.
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}]
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set total_pages_percent [percent $total_pages $file_pgcnt]
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set storage [expr {$total_pages*$pageSize}]
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set payload_percent [percent $payload $storage {of storage consumed}]
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set total_unused [expr {$ovfl_unused+$int_unused+$leaf_unused}]
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set avg_payload [divide $payload $nleaf]
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set avg_unused [divide $total_unused $nleaf]
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if {$int_pages>0} {
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # TODO: Is this formula correct?
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set nTab [mem eval "
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SELECT count(*) FROM (
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          SELECT DISTINCT tblname FROM space_used WHERE $where AND is_index=0
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      )
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "]
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set avg_fanout [mem eval "
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SELECT (sum(leaf_pages+int_pages)-$nTab)/sum(int_pages) FROM space_used
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          WHERE $where AND is_index = 0
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "]
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set avg_fanout [format %.2f $avg_fanout]
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set ovfl_cnt_percent [percent $ovfl_cnt $nleaf {of all entries}]
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Print out the sub-report statistics.
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline {Percentage of total database} $total_pages_percent
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline {Number of entries} $nleaf
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline {Bytes of storage consumed} $storage
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline {Bytes of payload} $payload $payload_percent
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline {Average payload per entry} $avg_payload
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline {Average unused bytes per entry} $avg_unused
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if {[info exists avg_fanout]} {
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    statline {Average fanout} $avg_fanout
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if {$total_pages>1} {
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set fragmentation [percent $gap_cnt [expr {$total_pages-1}] {fragmentation}]
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    statline {Fragmentation} $fragmentation
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline {Maximum payload per entry} $mx_payload
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline {Entries that use overflow} $ovfl_cnt $ovfl_cnt_percent
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if {$int_pages>0} {
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    statline {Index pages used} $int_pages
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline {Primary pages used} $leaf_pages
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline {Overflow pages used} $ovfl_pages
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline {Total pages used} $total_pages
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if {$int_unused>0} {
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set int_unused_percent \
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         [percent $int_unused [expr {$int_pages*$pageSize}] {of index space}]
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    statline "Unused bytes on index pages" $int_unused $int_unused_percent
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline "Unused bytes on primary pages" $leaf_unused \
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     [percent $leaf_unused [expr {$leaf_pages*$pageSize}] {of primary space}]
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline "Unused bytes on overflow pages" $ovfl_unused \
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     [percent $ovfl_unused [expr {$ovfl_pages*$pageSize}] {of overflow space}]
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline "Unused bytes on all pages" $total_unused \
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               [percent $total_unused $storage {of all space}]
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 1
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Calculate the overhead in pages caused by auto-vacuum.
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# This procedure calculates and returns the number of pages used by the
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# auto-vacuum 'pointer-map'. If the database does not support auto-vacuum,
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# then 0 is returned. The two arguments are the size of the database file in
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# pages and the page size used by the database (in bytes).
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)proc autovacuum_overhead {filePages pageSize} {
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Set $autovacuum to non-zero for databases that support auto-vacuum.
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set autovacuum [db one {PRAGMA auto_vacuum}]
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If the database is not an auto-vacuum database or the file consists
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # of one page only then there is no overhead for auto-vacuum. Return zero.
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if {0==$autovacuum || $filePages==1} {
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The number of entries on each pointer map page. The layout of the
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # database file is one pointer-map page, followed by $ptrsPerPage other
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # pages, followed by a pointer-map page etc. The first pointer-map page
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # is the second page of the file overall.
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set ptrsPerPage [expr double($pageSize/5)]
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Return the number of pointer map pages in the database.
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return [expr int(ceil( ($filePages-1.0)/($ptrsPerPage+1.0) ))]
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Calculate the summary statistics for the database and store the results
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# in TCL variables. They are output below. Variables are as follows:
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# pageSize:      Size of each page in bytes.
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# file_bytes:    File size in bytes.
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# file_pgcnt:    Number of pages in the file.
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# file_pgcnt2:   Number of pages in the file (calculated).
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# av_pgcnt:      Pages consumed by the auto-vacuum pointer-map.
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# av_percent:    Percentage of the file consumed by auto-vacuum pointer-map.
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# inuse_pgcnt:   Data pages in the file.
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# inuse_percent: Percentage of pages used to store data.
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# free_pgcnt:    Free pages calculated as (<total pages> - <in-use pages>)
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# free_pgcnt2:   Free pages in the file according to the file header.
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# free_percent:  Percentage of file consumed by free pages (calculated).
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# free_percent2: Percentage of file consumed by free pages (header).
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# ntable:        Number of tables in the db.
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# nindex:        Number of indices in the db.
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# nautoindex:    Number of indices created automatically.
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# nmanindex:     Number of indices created manually.
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# user_payload:  Number of bytes of payload in table btrees
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#                (not including sqlite_master)
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# user_percent:  $user_payload as a percentage of total file size.
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set file_bytes  [file size $file_to_analyze]
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set file_pgcnt  [expr {$file_bytes/$pageSize}]
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set av_pgcnt    [autovacuum_overhead $file_pgcnt $pageSize]
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set av_percent  [percent $av_pgcnt $file_pgcnt]
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set sql {SELECT sum(leaf_pages+int_pages+ovfl_pages) FROM space_used}
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set inuse_pgcnt   [expr int([mem eval $sql])]
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set inuse_percent [percent $inuse_pgcnt $file_pgcnt]
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set free_pgcnt    [expr $file_pgcnt-$inuse_pgcnt-$av_pgcnt]
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set free_percent  [percent $free_pgcnt $file_pgcnt]
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set free_pgcnt2   [db one {PRAGMA freelist_count}]
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set free_percent2 [percent $free_pgcnt2 $file_pgcnt]
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set file_pgcnt2 [expr {$inuse_pgcnt+$free_pgcnt2+$av_pgcnt}]
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set ntable [db eval {SELECT count(*)+1 FROM sqlite_master WHERE type='table'}]
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set nindex [db eval {SELECT count(*) FROM sqlite_master WHERE type='index'}]
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set sql {SELECT count(*) FROM sqlite_master WHERE name LIKE 'sqlite_autoindex%'}
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set nautoindex [db eval $sql]
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set nmanindex [expr {$nindex-$nautoindex}]
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# set total_payload [mem eval "SELECT sum(payload) FROM space_used"]
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set user_payload [mem one {SELECT int(sum(payload)) FROM space_used
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     WHERE NOT is_index AND name NOT LIKE 'sqlite_master'}]
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set user_percent [percent $user_payload $file_bytes]
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Output the summary statistics calculated above.
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts "/** Disk-Space Utilization Report For $file_to_analyze"
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)catch {
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  puts "*** As of [clock format [clock seconds] -format {%Y-%b-%d %H:%M:%S}]"
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts ""
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)statline {Page size in bytes} $pageSize
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)statline {Pages in the whole file (measured)} $file_pgcnt
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)statline {Pages in the whole file (calculated)} $file_pgcnt2
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)statline {Pages that store data} $inuse_pgcnt $inuse_percent
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)statline {Pages on the freelist (per header)} $free_pgcnt2 $free_percent2
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)statline {Pages on the freelist (calculated)} $free_pgcnt $free_percent
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)statline {Pages of auto-vacuum overhead} $av_pgcnt $av_percent
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)statline {Number of tables in the database} $ntable
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)statline {Number of indices} $nindex
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)statline {Number of named indices} $nmanindex
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)statline {Automatically generated indices} $nautoindex
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)statline {Size of the file in bytes} $file_bytes
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)statline {Bytes of user payload stored} $user_payload $user_percent
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Output table rankings
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts ""
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts "*** Page counts for all tables with their indices ********************"
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts ""
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)mem eval {SELECT tblname, count(*) AS cnt,
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              int(sum(int_pages+leaf_pages+ovfl_pages)) AS size
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          FROM space_used GROUP BY tblname ORDER BY size+0 DESC, tblname} {} {
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  statline [string toupper $tblname] $size [percent $size $file_pgcnt]
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Output subreports
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if {$nindex>0} {
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subreport {All tables and indices} 1
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)subreport {All tables} {NOT is_index}
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if {$nindex>0} {
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subreport {All indices} {is_index}
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)foreach tbl [mem eval {SELECT name FROM space_used WHERE NOT is_index
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       ORDER BY name}] {
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  regsub ' $tbl '' qn
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set name [string toupper $tbl]
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set n [mem eval "SELECT count(*) FROM space_used WHERE tblname='$qn'"]
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if {$n>1} {
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    subreport "Table $name and all its indices" "tblname='$qn'"
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    subreport "Table $name w/o any indices" "name='$qn'"
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    subreport "Indices of table $name" "tblname='$qn' AND is_index"
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    subreport "Table $name" "name='$qn'"
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Output instructions on what the numbers above mean.
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts {
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)*** Definitions ******************************************************
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Page size in bytes
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The number of bytes in a single page of the database file.
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Usually 1024.
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Number of pages in the whole file
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts \
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"    The number of $pageSize-byte pages that go into forming the complete
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    database"
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts \
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Pages that store data
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The number of pages that store data, either as primary B*Tree pages or
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    as overflow pages.  The number at the right is the data pages divided by
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    the total number of pages in the file.
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Pages on the freelist
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The number of pages that are not currently in use but are reserved for
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    future use.  The percentage at the right is the number of freelist pages
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    divided by the total number of pages in the file.
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Pages of auto-vacuum overhead
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The number of pages that store data used by the database to facilitate
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    auto-vacuum. This is zero for databases that do not support auto-vacuum.
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Number of tables in the database
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The number of tables in the database, including the SQLITE_MASTER table
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    used to store schema information.
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Number of indices
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The total number of indices in the database.
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Number of named indices
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The number of indices created using an explicit CREATE INDEX statement.
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Automatically generated indices
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The number of indices used to implement PRIMARY KEY or UNIQUE constraints
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    on tables.
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Size of the file in bytes
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The total amount of disk space used by the entire database files.
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Bytes of user payload stored
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The total number of bytes of user payload stored in the database. The
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    schema information in the SQLITE_MASTER table is not counted when
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    computing this number.  The percentage at the right shows the payload
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    divided by the total file size.
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Percentage of total database
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The amount of the complete database file that is devoted to storing
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    information described by this category.
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Number of entries
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The total number of B-Tree key/value pairs stored under this category.
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Bytes of storage consumed
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The total amount of disk space required to store all B-Tree entries
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    under this category.  The is the total number of pages used times
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    the pages size.
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Bytes of payload
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The amount of payload stored under this category.  Payload is the data
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    part of table entries and the key part of index entries.  The percentage
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    at the right is the bytes of payload divided by the bytes of storage
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    consumed.
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Average payload per entry
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The average amount of payload on each entry.  This is just the bytes of
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    payload divided by the number of entries.
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Average unused bytes per entry
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The average amount of free space remaining on all pages under this
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    category on a per-entry basis.  This is the number of unused bytes on
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    all pages divided by the number of entries.
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Fragmentation
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The percentage of pages in the table or index that are not
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    consecutive in the disk file.  Many filesystems are optimized
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for sequential file access so smaller fragmentation numbers
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sometimes result in faster queries, especially for larger
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    database files that do not fit in the disk cache.
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Maximum payload per entry
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The largest payload size of any entry.
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Entries that use overflow
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The number of entries that user one or more overflow pages.
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Total pages used
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This is the number of pages used to hold all information in the current
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    category.  This is the sum of index, primary, and overflow pages.
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Index pages used
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This is the number of pages in a table B-tree that hold only key (rowid)
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    information and no data.
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Primary pages used
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    This is the number of B-tree pages that hold both key and data.
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Overflow pages used
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The total number of overflow pages used for this category.
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Unused bytes on index pages
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The total number of bytes of unused space on all index pages.  The
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    percentage at the right is the number of unused bytes divided by the
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    total number of bytes on index pages.
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Unused bytes on primary pages
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The total number of bytes of unused space on all primary pages.  The
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    percentage at the right is the number of unused bytes divided by the
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    total number of bytes on primary pages.
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Unused bytes on overflow pages
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The total number of bytes of unused space on all overflow pages.  The
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    percentage at the right is the number of unused bytes divided by the
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    total number of bytes on overflow pages.
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Unused bytes on all pages
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The total number of bytes of unused space on all primary and overflow
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pages.  The percentage at the right is the number of unused bytes
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    divided by the total number of bytes.
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Output a dump of the in-memory database. This can be used for more
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# complex offline analysis.
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts "**********************************************************************"
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts "The entire text of this report can be sourced into any SQL database"
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts "engine for further analysis.  All of the text above is an SQL comment."
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts "The data used to generate this report follows:"
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts "*/"
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts "BEGIN;"
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts $tabledef
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)unset -nocomplain x
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)mem eval {SELECT * FROM space_used} x {
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  puts -nonewline "INSERT INTO space_used VALUES"
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set sep (
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  foreach col $x(*) {
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set v $x($col)
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if {$v=="" || ![string is double $v]} {set v [quote $v]}
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    puts -nonewline $sep$v
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set sep ,
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  puts ");"
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)puts "COMMIT;"
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} err]} {
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  puts "ERROR: $err"
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  puts $errorInfo
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit 1
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
627