7 sql2xls.pl - convert sql queries on file system to Excel file
11 Each file in current directory which ends in C<< *.sql >> will
12 be converted to Excel sheet. If you want to have specific order, you can
13 prefix filenames with numbers which will be striped when creating sheet
16 Comments in sql files (lines beginning with --) will be placed
17 in first line in bold.
19 To specify database on which SQL query is executed
20 C<< \c database >> syntax is supported.
22 You can also run script from command line, and it will produce
23 C<< sql_reports.xls >> file.
27 Only required file is this script C<< sql2xls.cgi >>
29 If your server is configured to execute C<.cgi> files, you can
30 drop this script anywhere, but you can also add something like
32 ScriptAlias /xls-reports /srv/SQL2XLS/sql2xls.cgi
34 in Apache's virtual host configuration to get nice URLs
38 Dobrica Pavlinusic, dpavlin@rot13.org
42 use Spreadsheet::WriteExcel;
44 use CGI::Carp qw(fatalsToBrowser);
45 use CGI qw(path_translated);
46 use Encode qw/decode/;
47 use Data::Dump qw/dump/;
49 # edit following to set defaults
50 our $dsn = 'DBI:Pg:dbname=';
51 our $database = 'template1';
52 our $user = 'dpavlin';
54 our $path = 'sql_reports.xls';
56 our $db_encoding = 'iso-8859-2';
57 our $xls_date_format = 'dd.mm.yyyy';
61 my $sql_dir = path_translated || '.';
62 $sql_dir =~ s,/[^/]+$,,;
64 opendir(DIR, $sql_dir) || die "can't opendir $sql_dir: $!";
65 my @sql_files = sort grep { /\.sql$/i && -f "$sql_dir/$_" } readdir(DIR);
68 my $config_path = "$sql_dir/config.pl";
69 warn "# using $config_path\n";
70 require $config_path if -e $config_path;
73 if ($ENV{GATEWAY_INTERFACE} && $ENV{GATEWAY_INTERFACE} =~ m/CGI/i) {
75 print "Content-type: application/vnd.ms-excel\n\n";
76 $workbook = Spreadsheet::WriteExcel->new("-");
78 # Create a new Excel workbook
79 $workbook = Spreadsheet::WriteExcel->new( $path );
80 warn "Creating XLS file $path\n";
83 my $date_format = $workbook->add_format(num_format => $xls_date_format);
85 my $dbh = DBI->connect($dsn . $database,$user,$passwd, { RaiseError => 1, AutoCommit => 0 }) || die $DBI::errstr;
88 return decode( $db_encoding, shift );
91 foreach my $sql_file (@sql_files) {
93 my $sheet_name = $sql_file;
94 $sheet_name =~ s/\d+_//;
95 $sheet_name =~ s/_/ /g;
96 $sheet_name =~ s/\.sql//;
99 warn "# clipping sheet name '$sheet_name' to 31 char limit\n" if length $sheet_name > 31;
100 my $worksheet = $workbook->addworksheet( substr($sheet_name,0,31) );
102 print STDERR "working on $sql_file\n" if ($debug);
104 open(SQL,$sql_file) || die "can't open sql file '$sql_file': $!";
109 if (/^\\c\s+(\S+)/) {
110 $dbh->disconnect if $dbh;
111 print STDERR "## connect to $1\n" if $debug;
112 $dbh = DBI->connect($dsn . $1,$user,$passwd, { RaiseError => 1, AutoCommit => 0 }) || die $DBI::errstr;
113 } elsif (/^--(.+)/) {
121 $sql =~ s/\s\s+/ /gs;
123 print STDERR "sql: $sql\ncomment: $comment\n" if ($debug);
129 # Add and define a format
130 my $fmt_comment = $workbook->addformat(); # Add a format
131 $fmt_comment->set_bold();
133 $comment =~ s/^\s+//;
134 $comment =~ s/\s+$//;
136 $worksheet->write($row, 0, _c($comment), $fmt_comment);
140 my $sth = $dbh->prepare($sql);
143 my $fmt_header = $workbook->addformat(); # Add a format
144 $fmt_header->set_italic();
146 for(my $col=0; $col<=$#{ $sth->{NAME} }; $col++) {
147 $worksheet->write($row, $col, ${ $sth->{NAME} }[$col], $fmt_header);
151 my @types = map { $dbh->type_info($_) ? $dbh->type_info($_)->{TYPE_NAME} : '?' } @{ $sth->{TYPE} };
153 while (my @row = $sth->fetchrow_array() ) {
154 for(my $col=0; $col<=$#row; $col++) {
155 my $data = $row[$col];
156 if ( $types[$col] =~ m/^date/i ) {
157 $data .= 'T' if $data =~ m/^\d\d\d\d-\d\d-\d\d$/;
158 $data =~ s/^(\d\d\d\d-\d\d-\d\d)\s(\d\d:\S+)$/$1T$2/;
160 $worksheet->write_date_time( $row, $col, $data, $date_format );
162 $worksheet->write($row, $col, _c( $data ) );