#!/usr/bin/env perl # # $Id$ use strict; use warnings; use Getopt::Std; use Text::CSV; use DBI; use DBD::CSV; # I/O is assumed to be in UTF-8 use open ':encoding(UTF-8)'; binmode( STDIN, ':utf8' ); binmode( STDOUT, ':utf8' ); my %opt; getopts( 'alhe:q:st:u:v', \%opt ); HELP_MESSAGE() if $opt{h}; my $add_column = $opt{a} || 0; my $in_quote = $opt{q} // '"'; length($in_quote) eq 1 or puke('-q must be followed by a single character'); my $in_sep = $opt{t} // ','; length($in_sep) eq 1 or puke('-t must be followed by a single character'); my $out_sep = $opt{u} // $in_sep; length($out_sep) eq 1 or puke('-u must be followed by a single character'); my $escape = $opt{e} // '\\'; my $use_sqlite = $opt{l} // 0; my $sql = shift(@ARGV) or puke('you must supply a SQL statement as the (first) argument'); sub HELP_MESSAGE { print STDERR <connect( 'dbi:CSV:', undef, undef, { f_lockfile => 0, csv_sep_char => $in_sep, csv_quote_char => $in_quote, csv_escape_char => $escape, #csv_tables => { map { 't' . ($_+1) => { file => $ARGV[$_] } } 0 .. $#ARGV } } ) or puke('cannot create CSV reader or open input file(s)'); # setting csv_tables there doesn't work well enough, so #$csvdbh->{csv_tables}{io} = { file => '-' }; # does that work?? untested $csvdbh->{csv_tables}{ 't' . ( $_ + 1 ) } = { file => $ARGV[$_], $add_column ? ( col_names => [] ) : () } for 0 .. $#ARGV; my $csvw = Text::CSV->new( { quote_space => 0, sep_char => $out_sep, eol => $/ } ) or puke('CSV writer creation failed'); # not quite correct: we should take all settings from $csvdbh, e.g. encoding my $qdbh; if ( !$use_sqlite ) { $qdbh = $csvdbh; } else { $qdbh = DBI->connect( 'dbi:SQLite:dbname=', '', '', { RaiseError => 1, AutoCommit => 0 } ) or puke('cannot open a temporary SQLite database - retry without -l?'); # copy the input to the database foreach my $tablename ( keys %{ $csvdbh->{csv_tables} } ) { my $sth = $csvdbh->prepare("SELECT * FROM $tablename") or puke("unexpected error: cannot prepare import of table $tablename"); $sth->execute() or puke("unexpected error: cannot execute import of table $tablename"); my $rows = $sth->fetchall_arrayref() or puke("unexpected error: cannot fetch import of table $tablename"); insert_table( $qdbh, $tablename, $sth->{NAME}, $rows ); } } sub insert_table { my ( $dbh, $tablename, $colnames, $rows ) = @_; my @colnames = @$colnames; my $sth = $dbh->do( sprintf( 'CREATE TABLE "%s" (%s)', $tablename, join( ',', map { sprintf( '"%s" %s', $colnames[$_], 'TEXT' ) } 0 .. $#colnames ) ) ) or puke("cannot create table $tablename"); my $inserter = $dbh->prepare( sprintf( 'INSERT INTO "%s" (%s) VALUES (%s)', $tablename, join( ',', map { sprintf( '"%s"', $_ ) } @colnames ), join( ',', map { '?' } @colnames ) ) ) or puke("cannot prepare insertion of table $tablename"); $dbh->commit(); foreach my $row (@$rows) { if ( !$inserter->execute(@$row) ) { puke( "cannot insert a row into $tablename: ", $dbh->errstr ); } } $dbh->commit(); } $sql =~ s/"/'/g if $opt{s}; warn "$sql\n" if $opt{v}; my $qsth = $qdbh->prepare($sql) or puke('SQL statement preparation failed'); $qsth->execute or puke('SQL statement execution failed'); #$qdbh->commit(); AutoCommit is on $csvw->print( *STDOUT, $qsth->{NAME} ); while ( my $r = $qsth->fetchrow_arrayref ) { $csvw->print( *STDOUT, $r ); } $qsth->finish(); $qdbh->disconnect();