#!/usr/bin/perl use charnames ':full'; # only necessary before v5.16 use strict; use v5.10; use warnings; use autodie; use experimental qw(autoderef); use DBIx::SimpleConnect; use Encode qw(:fallbacks encode); use Getopt::Long; use Pod::Usage; use POSIX qw(strftime); my $db = "default"; my $dir = "."; GetOptions( 'dbname=s' => \$db, 'directory=s' => \$dir, ) or pod2usage(1); binmode STDOUT, ":encoding(UTF-8)"; { no autodie; unless (chdir($dir)) { # if at first we don't succeed ... mkdir $dir or die "cannot create $dir: $!"; chdir $dir or die "cannot chdir to $dir: $!"; } } my $errorfile = strftime("%Y-%m-%d %H:%M:%S%z.err", localtime); open STDERR, ">:encoding(UTF-8)", $errorfile; my $dbh = DBIx::SimpleConnect->connect($db, {RaiseError => 1, PrintError => 0}); # this is intended to run from cron, so limit the time it can run. $dbh->do("set statement_timeout to 30000"); my $overview = $dbh->selectall_hashref(" select schemaname, tablename, sum(pg_total_relation_size(quote_ident(schemaname) || '.' || quote_ident(tablename))) as total_size, sum(pg_table_size(quote_ident(schemaname) || '.' || quote_ident(tablename))) as table_size, sum(pg_indexes_size(quote_ident(schemaname) || '.' || quote_ident(tablename))) as indexes_size from pg_tables group by rollup (schemaname, tablename) ", ['schemaname', 'tablename'] ); open(my $fh, ">:encoding(UTF-8)", "index.html.$$"); print_header($fh); say $fh "

Disk usage for database $db

"; say $fh "
", strftime("%Y-%m-%d %H:%M:%S%z", localtime), "
"; say $fh ""; say $fh ""; say $fh ""; say $fh ""; say $fh ""; say $fh ""; say $fh ""; say $fh ""; for my $s (sort { $overview->{$b}{''}{total_size} <=> $overview->{$a}{''}{total_size} } keys $overview) { say $fh ""; say $fh ""; say $fh ""; say $fh ""; say $fh ""; say $fh ""; say $fh ""; for my $t (sort { $overview->{$s}{$b}{total_size} <=> $overview->{$s}{$a}{total_size} } keys $overview->{$s}) { next unless $t; # schema - we already have that say $fh ""; say $fh ""; say $fh ""; say $fh ""; say $fh ""; say $fh ""; say $fh ""; } } say $fh "
", "Schema", "", "Table", "", "Total size (bytes)", "", "Table size (bytes)", "", "Indexes size (bytes)", "
", escape($s), "", '', "", pretty($overview->{$s}{''}{total_size}), "", pretty($overview->{$s}{''}{table_size}), "", pretty($overview->{$s}{''}{indexes_size}), "
", escape($s), "", escape($t), "", pretty($overview->{$s}{$t}{total_size}), "", pretty($overview->{$s}{$t}{table_size}), "", pretty($overview->{$s}{$t}{indexes_size}), "
"; close($fh); rename "index.html.$$", "index.html"; unlink($errorfile); # if we get here, we are fine exit(0); sub pretty { my ($n) = @_; while ($n =~ /[0-9]{4}/) { $n =~ s/([0-9]+)([0-9]{3})/$1\N{NARROW NO-BREAK SPACE}$2/; } return $n; } sub escape { my ($s) = @_; $s =~ s/[<>&'"]/sprintf("&#%d;", ord($1))/eg; return $s; } sub print_header { my ($fh) = @_; say $fh ""; say $fh ""; } # vim: sw=4 tw=132 expandtab