#!/opt/tcl803/bin/sytcl
#
# $Header: /home/cvsroot/tcldb/sybdump/sybdisk,v 1.7 1999/06/15 19:45:16 de Exp $

# This app tells you all about your disk usage for any given server.
#
# It is a descendant of the old EIS "sqllocate" tool.
#
# The report comes in several sections.  It shows you the disk
# devices and how much of each is allocated to each database,
# how much free.
# It shows you the database and how much of each is free, how much
# allocated to tables.
# It shows you the tables and how much space each one takes up and
# what percentage of the db space it represents, and how much of its
# index space is allocated/used.
#

set tcl_precision 4

lassign $argv server

set tclshare /opt/share/tcl
set ucodb $tclshare/lib/ucodb
loadlibindex $ucodb/ucodb.tlib
global server
global dbpipe1
set uname sa
puts stdout "Please enter the SA password for server $server"
gets stdin pass
set dbpipe1 [sybOpen master $uname $pass $server]
global sybmsg
#
set ofp [open sybdisk.$server w]
######################################################################
## clean out tempdb
set sqlcmd "dump tran tempdb with no_log"
doSQL 1
#
# GET THE PHYSICAL DEVICES
#
# index by physical name?
#
puts stderr "Get Physical Devices for $server"
set sqlcmd "select name, phyname, size = high-low, low 
from master.dbo.sysdevices 
where status&2 = 2 
order by name,low "
set sqt sysdevices
doSQL 1
set cols [sybCols 1]
# returns name, phyname, size, low
while {1} {
        set line [sybNext 1]
        if {$line == ""} {break}
	eval lassign \$line $cols
	set mb [expr $size/512.0]
	keylset Disks($name) phyname $phyname size $mb low $low
}
#
######################################################################
# GET SEG LIST FOR EACH DB
#
puts stderr "Get Segments for each DB on $server"
set sqlcmd "select name=b.name, b.dbid, owner_name=suser_name(suid),  
d.phyname, 
    b.status, device   = d.name ,  usage = u.segmap ,  
    lstart = u.lstart, segsize =  u.size , u.vstart,
    pagesize = x.low  
from master.dbo.sysdatabases b ,master.dbo.sysusages u ,  
master.dbo.sysdevices d ,master.dbo.spt_values  x  
where   b.dbid   = u.dbid  
    and d.low   <= u.size + u.vstart  
    and d.high  >= u.size + u.vstart - 1  
    and d.status & 2 = 2  
    and x.type   = 'E'  
    and x.number = 1"
set sqt [list sysdatabases sysusages sysdevices spt_values]
doSQL 1
set cols [sybCols 1]
# returns name, dbid, owner_name, phyname, status, device, usage, lstart, segsize, vstart, pagesize
while {1} {
        set line [sybNext 1]
        if {$line == ""} {break}
	set i 1
	eval lassign \$line $cols
	set segMB [expr $segsize/512.0]
	if {![info exists Device($name)]} {
	puts stderr "NEW DATABASE $name"
	set Device($name) $device
	keylset Databases($name) dbid $dbid owner $owner_name segct $i
	keylset Segments($name,$i) device $device phy $phyname lstart $lstart segsz $segsize vstart $vstart pgsiz $pagesize segMB $segMB 
	set DBsize($name) $segMB
	puts stderr "keylset DiskUse($device) $name $segMB"
	keylset DiskUse($device) $name $segMB
	} else {
	lappend Device($name) $device
	while {[info exists Segments($name,$i)]} {
		incr i
	}
	keylset Segments($name,$i) device $device phy $phyname  lstart $lstart segsz $segsize vstart $vstart pgsiz $pagesize segMB $segMB 
	keylset Databases($name) segct $i
	set DBsize($name) [expr $DBsize($name) + $segMB]
	set err [catch {set dshare [keylget DiskUse($device) $name]} ]
	if {$err} {
	keylset DiskUse($device) $name $segMB
	} else {
	keylset DiskUse($device) $name [expr $dshare + $segMB]
	}
	}
}
#
######################################################################
# GET TOTAL SIZE for each database
#
puts stderr "Get size of each DB on $server"
set sqlcmd "select distinct
    dbid = d.dbid,
  d.name,
    db_size = sum(u.size)
    from master.dbo.sysusages u, master.dbo.sysdatabases d
    where d.dbid = u.dbid     group by d.dbid"

set sqt [list sysdatabases sysusages]
doSQL 1
set cols [sybCols 1]
# returns  dbid, name, size 
while {1} {
        set line [sybNext 1]
        if {$line == ""} {break}
	lassign $line dbid name size
	set size [expr $size/512.0]
	keylset Databases($name) totMB $size
	puts stderr "$name total size $size, total of segs is $DBsize($name)"
}
#
######################################################################
# NOW FOREACH DATABASE GET ITS TABLES
#
puts stderr "Get table inventory for each DB on $server"
foreach db [lsort [array names Databases]] {
puts stderr "... $db"

sybuse $dbpipe1 $db

set sqlcmd "select o.id, o.name, i.name, type, user_name=user_name(uid), 
segment , indid 
from $db..sysobjects o, $db..sysindexes i 
where o.id =  i.id 
and type in ('U', 'S')"

set sqt [list $db..sysobjects $db..sysindexes]
doSQL 1
set cols [sybCols 1]
# returns name, dbid, name, size
while {1} {
        set line [sybNext 1]
        if {$line == ""} {break}
	lassign $line id name name2 type owner segid indid
	if {$indid} {
		keylset Indices($db,$name,$indid) iname $name2 
	} 
	if {![info exists Tables($db,$name)]} {
		lappend DBtables($db) $name
		keylset Tables($db,$name) id $id type $type owner $owner segid $segid 
#		puts stderr "$db,$name is owned by $owner ($db.$owner.$name)"
	}
}
}
#
#
######################################################################
# NOW FOREACH TABLE GET ITS SIZE (TEDIOUS)
#
puts stderr "Get size of each table in each DB on $server"
foreach db [lsort [array names Databases]] {
	puts stderr "... $db"
	sybuse $dbpipe1 $db
	set Reserved($db) 0
	foreach t $DBtables($db) {
		set o [keylget Tables($db,$t) owner]
		set sqlcmd "sp_spaceused '$o.$t'"
		set res [doSQL 1]
		set line [sybNext 1]
		if {$line == ""} {
			puts stderr "OUCH error, in $db no result from $sqlcmd"
			puts stderr "$res"
			continue
		} else {
#			puts stderr "OK, in $db got sp_spaceused data from $sqlcmd"
#			puts stderr "$line"
		}
		lassign $line name rows reserved data index unused
		set reserved [lindex $reserved 0]
		set data [lindex $data 0]
		set index [lindex $index 0]
		set unused [lindex $unused 0]
		if {[crange $reserved 0 2] == "Not"} {set reserved ???}
		if {[crange $unused 0 2] == "Not"} {set unused ???}
		if {[crange $data 0 2] == "Not"} {set data ???}
		if {[crange $index 0 2] == "Not"} {set index ???}
		keylset Tables($db,$t) rows $rows resK $reserved dataK $data indexK $index freeK $unused
		catch {set Reserved($db) [expr $Reserved($db) + $reserved]}
		if {$db == "catalogs"} {
			puts stderr "--- Table $t reserved $reserved, add to total for $Reserved($db)"
		}
	}
}
#
puts stderr "DONE with data collection, now summarize and report"
#
# NOW YOU HAVE ALL THE DATA YOU EVER WANTED AND MORE...
#	try to present it in some reasonable fashion
#
#
#	First, the devices
#
puts $ofp "SYBASE SERVER $server DISK SPACE REPORT [clock format [clock seconds]]\n\n"
puts $ofp "Sybase Server $server has the following devices:\n"
foreach d [lsort [array names Disks]] {
	set size [keylget Disks($d) size]
	set phys [keylget Disks($d) phyname]
	puts $ofp "$d (physical name $phys), $size MB"
	set dst 0
	foreach k [lsort [keylkeys DiskUse($d)]] {
		set dbs [keylget DiskUse($d) $k]
		puts $ofp "\t $k : $dbs MB"
		set dst [expr $dst + $dbs]
	}
	set free [expr $size - $dst]
	puts $ofp "USED $dst, FREE $free\n"
}
#
flush $ofp
puts $ofp "\f"
#
puts $ofp "SYBASE SERVER $server DISK SPACE REPORT [clock format [clock seconds]]\n\n"
puts $ofp "Sybase Server $server has the following databases:\n"
foreach db [lsort [array names Databases]] {

	set segs [keylget Databases($db) segct]
	set dbid [keylget Databases($db) dbid]
	set own [keylget Databases($db) owner]	
	set siz [keylget Databases($db) totMB]	

	set res [expr $Reserved($db)/1024.0]
	set free [expr $siz - $res]
	puts $ofp "$db (owned by $own) $siz MB, $res MB reserved, $free FREE\n"
	puts $ofp "  segments:"
	foreach s [lsort [array names Segments $db,*]] {
	foreach k [keylkeys Segments($s)] {
		set $k [keylget Segments($s) $k]
	}
	puts $ofp "\ton $device ($phy), $db uses $segMB MB"
	}
	puts $ofp ""
}
#
foreach db [lsort [array names Databases]] {
flush $ofp
puts $ofp "\f"
puts $ofp "SYBASE SERVER $server DISK SPACE REPORT [clock format [clock seconds]]\n\n"
puts $ofp "DATABASE $db TABLES by DATA SIZE\n\n"
#
#
#keylset Tables($db,$t) rows $rows resK $reserved dataK $data indexK $index freeK $unused
#keylset Tables($db,$name) id $id name2 $name2 type $type owner $owner segid $segid indid $indid

catch {unset BySize}
foreach t [array names Tables $db,*] {
	set err [catch {set ds [keylget Tables($t) dataK]} res]
	if {$err} {
		puts stderr "ERROR getting dataK from Tables $t"
		continue
	}
#	puts stderr "Add $t to BySize ( $ds )"
	lappend BySize($ds) $t
}
set tdata 0
set tresv 0
set tindx 0
set tfree 0
foreach e [lsort -integer -decreasing [array names BySize]] {
	foreach t $BySize($e) {
	lassign [split $t ,] junk tn
	foreach k [keylkeys Tables($t)] {
		set $k [keylget Tables($t) $k]
	}
#	puts stderr "$owner.$tn $rows rows $dataK dat $indexK idx $resK rsv $freeK free"
#	set err [catch {puts $ofp "[format "%s (%06d rows)\n\tuses (K) %06d data %06d index out of %06d rsvd for %06d FREE" $owner.$t $rows $dataK $indexK $resK $freeK]"} res]
	set err [catch {puts $ofp "[format "%s (%s rows) \n\tuses (K) %6.6s data %6.6s index out of %6.6s rsvd for %6.6s FREE\n" $owner.$tn $rows $dataK $indexK $resK $freeK]"} res]

	set tdata [expr $tdata + $dataK]
	set tresv [expr $tresv + $resK]
	set tindx [expr $tindx + $indexK]
	catch {set tfree [expr $tfree + $freeK]}
	}
}
puts $ofp "\n\nTOTAL DATA SPACE $tdata K, INDEX SPACE $tindx K, RSVD SPACE $tresv K, FREE SPACE $tfree K\n\n"
}
#
close $ofp
