diff --git a/autom4te.cache/requests b/autom4te.cache/requests index 0cc71478..8e58b38e 100644 --- a/autom4te.cache/requests +++ b/autom4te.cache/requests @@ -64,8 +64,8 @@ 'AM_SET_LEADING_DOT' => 1, 'AM_SET_DEPDIR' => 1, '_AM_DEPENDENCIES' => 1, - 'AM_PROG_INSTALL_SH' => 1, 'm4_include' => 1, + 'AM_PROG_INSTALL_SH' => 1, '_AC_AM_CONFIG_HEADER_HOOK' => 1, 'AU_DEFUN' => 1, 'AM_MAKE_INCLUDE' => 1 @@ -83,25 +83,25 @@ 'configure.ac' ], { - '_LT_AC_TAGCONFIG' => 1, 'AM_PROG_F77_C_O' => 1, - 'm4_pattern_forbid' => 1, + '_LT_AC_TAGCONFIG' => 1, 'AC_INIT' => 1, - '_AM_COND_IF' => 1, + 'm4_pattern_forbid' => 1, 'AC_CANONICAL_TARGET' => 1, - 'AC_SUBST' => 1, + '_AM_COND_IF' => 1, 'AC_CONFIG_LIBOBJ_DIR' => 1, - 'AC_FC_SRCEXT' => 1, + 'AC_SUBST' => 1, 'AC_CANONICAL_HOST' => 1, + 'AC_FC_SRCEXT' => 1, 'AC_PROG_LIBTOOL' => 1, 'AM_INIT_AUTOMAKE' => 1, 'AC_CONFIG_SUBDIRS' => 1, 'AM_AUTOMAKE_VERSION' => 1, 'LT_CONFIG_LTDL_DIR' => 1, - 'AC_REQUIRE_AUX_FILE' => 1, 'AC_CONFIG_LINKS' => 1, - 'm4_sinclude' => 1, + 'AC_REQUIRE_AUX_FILE' => 1, 'LT_SUPPORTED_TAG' => 1, + 'm4_sinclude' => 1, 'AM_MAINTAINER_MODE' => 1, 'AM_GNU_GETTEXT_INTL_SUBDIR' => 1, '_m4_warn' => 1, @@ -114,17 +114,17 @@ 'include' => 1, 'AM_GNU_GETTEXT' => 1, 'AC_LIBSOURCE' => 1, - 'AM_PROG_FC_C_O' => 1, 'AC_CANONICAL_BUILD' => 1, + 'AM_PROG_FC_C_O' => 1, 'AC_FC_FREEFORM' => 1, 'AH_OUTPUT' => 1, - '_AM_SUBST_NOTMAKE' => 1, 'AC_CONFIG_AUX_DIR' => 1, - 'sinclude' => 1, - 'm4_pattern_allow' => 1, + '_AM_SUBST_NOTMAKE' => 1, 'AM_PROG_CC_C_O' => 1, - 'AC_CANONICAL_SYSTEM' => 1, + 'm4_pattern_allow' => 1, + 'sinclude' => 1, 'AM_CONDITIONAL' => 1, + 'AC_CANONICAL_SYSTEM' => 1, 'AC_CONFIG_HEADERS' => 1, 'AC_DEFINE_TRACE_LITERAL' => 1, 'm4_include' => 1, diff --git a/doc/FUTURE_IDEAS b/doc/FUTURE_IDEAS index 6f2aca0c..ccb1fd2a 100644 --- a/doc/FUTURE_IDEAS +++ b/doc/FUTURE_IDEAS @@ -1,13 +1,9 @@ -Bugfix: --eta/--progress with 0 jobs gave division by zero. -Bugfix in Makefile by Piotr Jaroszyński

- fex syntax for splitting fields http://www.semicomplete.com/projects/fex/ sql :foo 'select * from bar' | parallel --fex '|{1,2}' do_stuff {2} {1} Import sql -inputfile tabel, Split colonner til {n} sql :foo 'select * from bar' | parallel --colsep '\|' do_stuff {4} {1} diff --git a/doc/release_new_version b/doc/release_new_version index 9ac34da6..d31b510a 100644 --- a/doc/release_new_version +++ b/doc/release_new_version @@ -89,7 +89,7 @@ to:parallel@gnu.org, bug-parallel@gnu.org, info-gnu@gnu.org, bug-directory@gnu.o cc:Peter Simons , Sandro Cazzaniga , Tim Cuthbertson , Ludovic Courtès , Markus Ammer , Pavel Nuzhdin , - Phil Sung + Phil Sung , Michael Shigorin Subject: GNU Parallel 20100822 released @@ -99,7 +99,9 @@ download at: http://ftp.gnu.org/gnu/parallel/ New in this release: * First community generated bugfixes - Piotr Jaroszyński + +* Alt Linux package of GNU Parallel. Thanks to Michael Shigorin = About GNU Parallel = diff --git a/src/Makefile.am b/src/Makefile.am index 900d7850..8b3c23e8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ -bin_SCRIPTS = parallel sem -man_MANS = parallel.1 sem.1 -doc_DATA = parallel.html sem.html +bin_SCRIPTS = parallel sem sql +man_MANS = parallel.1 sem.1 sql.1 +doc_DATA = parallel.html sem.html sql.html parallel.1: parallel Makefile pod2man --release='$(PACKAGE_VERSION)' --center='$(PACKAGE_NAME)' \ @@ -10,6 +10,10 @@ sem.1: sem.pod Makefile pod2man --release='$(PACKAGE_VERSION)' --center='$(PACKAGE_NAME)' \ --section=1 $(srcdir)/sem.pod > $(srcdir)/sem.1 +sql.1: sql Makefile + pod2man --release='$(PACKAGE_VERSION)' --center='$(PACKAGE_NAME)' \ + --section=1 $(srcdir)/sql > $(srcdir)/sql.1 + parallel.html: parallel Makefile pod2html $(srcdir)/parallel > $(srcdir)/parallel.html rm -f $(srcdir)/pod2htm* @@ -18,8 +22,12 @@ sem.html: sem.pod Makefile pod2html $(srcdir)/sem.pod > $(srcdir)/sem.html rm -f $(srcdir)/pod2htm* +sql.html: sql Makefile + pod2html $(srcdir)/sql > $(srcdir)/sql.html + rm -f $(srcdir)/pod2htm* + sem: parallel ln -fs parallel sem -DISTCLEANFILES = parallel.1 sem.1 parallel.html sem.html -EXTRA_DIST = parallel sem parallel.1 sem.1 parallel.html sem.html sem.pod +DISTCLEANFILES = parallel.1 sem.1 sql.1 parallel.html sem.html sql.html +EXTRA_DIST = parallel sem sql parallel.1 sem.1 sql.1 parallel.html sem.html sem.pod sql.html diff --git a/src/Makefile.in b/src/Makefile.in index e1d6f61a..07a0a824 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -146,11 +146,11 @@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -bin_SCRIPTS = parallel sem -man_MANS = parallel.1 sem.1 -doc_DATA = parallel.html sem.html -DISTCLEANFILES = parallel.1 sem.1 parallel.html sem.html -EXTRA_DIST = parallel sem parallel.1 sem.1 parallel.html sem.html sem.pod +bin_SCRIPTS = parallel sem sql +man_MANS = parallel.1 sem.1 sql.1 +doc_DATA = parallel.html sem.html sql.html +DISTCLEANFILES = parallel.1 sem.1 sql.1 parallel.html sem.html sql.html +EXTRA_DIST = parallel sem sql parallel.1 sem.1 sql.1 parallel.html sem.html sem.pod sql.html all: all-am .SUFFIXES: @@ -451,6 +451,10 @@ sem.1: sem.pod Makefile pod2man --release='$(PACKAGE_VERSION)' --center='$(PACKAGE_NAME)' \ --section=1 $(srcdir)/sem.pod > $(srcdir)/sem.1 +sql.1: sql Makefile + pod2man --release='$(PACKAGE_VERSION)' --center='$(PACKAGE_NAME)' \ + --section=1 $(srcdir)/sql > $(srcdir)/sql.1 + parallel.html: parallel Makefile pod2html $(srcdir)/parallel > $(srcdir)/parallel.html rm -f $(srcdir)/pod2htm* @@ -459,6 +463,10 @@ sem.html: sem.pod Makefile pod2html $(srcdir)/sem.pod > $(srcdir)/sem.html rm -f $(srcdir)/pod2htm* +sql.html: sql Makefile + pod2html $(srcdir)/sql > $(srcdir)/sql.html + rm -f $(srcdir)/pod2htm* + sem: parallel ln -fs parallel sem diff --git a/src/sql b/src/sql new file mode 100755 index 00000000..838f6b7c --- /dev/null +++ b/src/sql @@ -0,0 +1,697 @@ +#!/usr/bin/perl -w + +=head1 NAME + +sql - execute a command on a database determined by a dburl + +=head1 SYNOPSIS + +B [B<-hnr>] [B<--table-size>] [B<--db-size>] [B<-p> I] [B<-s> I] I [I] + + +=head1 DESCRIPTION + +GNU B aims to give a simple, unified interface for accessing +databases through all the different databases' command line +clients. So far the focus has been on giving a common way to specify +login information (protocol, username, password, hostname, and port +number), size (database and table size), and running queries. + +The database is addressed using a dburl. If I is left out you +will get that database's interactive shell. + +GNU B is often used in combination with GNU B. + +=over 9 + +=item I + +A DBURL has the following syntax: +vendor://[user[:password]@][host][:port]/[database] + +See the section DBURL below. + +=item I + +The SQL command to run. + +Example: select 1+2 + +If you use * or ? remember to quote them, as the shell may otherwise +expand them. + +If no command is given SQL is read from the keyboard or STDIN. + +Example: echo 'select 1+2' | sql mysql:/// + + +=item B<--db-size> + +=item B<--dbsize> + +Size of database. Show the size of the database on disk. + + +=item B<--help> + +=item B<-h> + +Print a summary of the options to GNU B and exit. + + +=item B<--html> + +HTML output. Turn on HTML tabular output. + + +=item B<--show-processlist> + +=item B<--proclist> + +=item B<--listproc> + +Show the list of running queries. + + +=item B<--noheaders> + +=item B<--no-headers> + +=item B<-n> + +Remove headers and footers and print only tuples. Bug in Oracle: it +still prints number of rows found. + + +=item B<-p> I + +The string following -p will be given to the database connection +program as arguments. Multiple -p's will be joined with +space. Example: + +I<-p "-U username"> can also be written I<-p -U -p username>. + + +=item B<-r> + +Try 3 times. Short version of I<--retries 3>. + + +=item B<--retries> I + +Try I times. If the client program returns with an error, +retry the command. Default is I<--retries 1>. + + +=item B<--sep> I + +=item B<-s> I + +Field separator. Use I as seperator between columns. + + +=item B<--table-size> + +=item B<--tablesize> + +Size of tables. Show the size of the tables in the database. + + +=item B<--version> + +=item B<-V> + +Print the version GNU B and exit. + + +=back + +=head1 DBURL + +A DBURL has the following syntax: +vendor://[user[:password]@][host][:port]/[database] + +Examples: + mysql://user:pa55w0rd@mysqlserver/database + oracle://scott:tiger@oracleserver/xe + postgresql://user:pa55w0rd@pg.example.com/db + pg:/// + +Currently supported vendors: MySQL (mysql), MySQL with SSL (mysqls, +mysqlssl), Oracle (oracle, ora), PostgreSQL (postgresql, pg, pgsql, +postgres), PostgreSQL with SSL (postgresqlssl, pgs, pgsqlssl, +postgresssl, pgssl, postgresqls, pgsqls, postgress) + +Aliases must start with ':' and is read from +dburl.aliases.dist, dburl.aliases and ~/.dburl.aliases. The user's own +~/.dburl.aliases should only be readable by the user. + +Example of dburl.aliases: + + :myalias1 pg://user:pa55w0rd@pg.example.com/db + :myalias2 ora://scott:tiger@ora1.example.com/xe + # Short form of mysql://`whoami`:nopassword@localhost:3306/`whoami` + :myalias3 mysql:/// + # Short form of mysql://`whoami`:nopassword@localhost:33333/mydb + :myalias4 mysql://:33333/mydb + # Alias for an alias + :m :myalias4 + + +=head1 EXAMPLES + +=head2 Copy a PostgreSQL database + +pg_dump my_database | sql pg://user:pass@pgserver/my_new_db + +=head2 Empty all tables in a MySQL database + +sql -n mysql:/// 'show tables' | parallel sql mysql:/// delete from {}; + +=head2 Drop all tables in a PostgreSQL database + +sql -n pg:/// '\dt' | awk '{print $3}' | parallel -r sql pg:/// drop table {}; + +=head1 REPORTING BUGS + +GNU B is part of GNU B. Report bugs to . + + +=head1 AUTHOR + +Copyright (C) 2008,2009,2010 Ole Tange, Ange Optimization http://ange.dk + +=head1 LICENSE + +Copyright (C) 2007,2008,2009,2010 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +at your option any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +=head2 Documentation license I + +Permission is granted to copy, distribute and/or modify this documentation +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, with no Front-Cover Texts, and with no Back-Cover +Texts. A copy of the license is included in the file fdl.txt. + +=head2 Documentation license II + +You are free: + +=over 9 + +=item B + +to copy, distribute and transmit the work + +=item B + +to adapt the work + +=back + +Under the following conditions: + +=over 9 + +=item B + +You must attribute the work in the manner specified by the author or +licensor (but not in any way that suggests that they endorse you or +your use of the work). + +=item B + +If you alter, transform, or build upon this work, you may distribute +the resulting work only under the same, similar or a compatible +license. + +=back + +With the understanding that: + +=over 9 + +=item B + +Any of the above conditions can be waived if you get permission from +the copyright holder. + +=item B + +Where the work or any of its elements is in the public domain under +applicable law, that status is in no way affected by the license. + +=item B + +In no way are any of the following rights affected by the license: + +=over 9 + +=item * + +Your fair dealing or fair use rights, or other applicable +copyright exceptions and limitations; + +=item * + +The author's moral rights; + +=item * + +Rights other persons may have either in the work itself or in +how the work is used, such as publicity or privacy rights. + +=back + +=item B + +For any reuse or distribution, you must make clear to others the +license terms of this work. + +=back + +A copy of the full license is included in the file as cc-by-sa.txt. + +=head1 DEPENDENCIES + +GNU B uses Perl. If B is installed, MySQL dburls will +work. If B is installed, PostgreSQL dburls will work. If +B is installed, Oracle dburls will work. If B is +installed, GNU B will have a command history for Oracle. + + +=head1 FILES + +~/.dburl.aliases - user's own aliases with DBURLs + +dburl.aliases - user's own aliases with DBURLs + +dburl.aliases.dist - common aliases with DBURLs + + +=head1 SEE ALSO + +B(1), B(1), B(1), B(1) + +=cut + +use Getopt::Long; +use strict; +Getopt::Long::Configure ("bundling","require_order"); +GetOptions("passthrough|p=s@" => \$::opt_p, + "sep|s=s" => \$::opt_s, + "html" => \$::opt_html, + "show-processlist|proclist|listproc" => \$::opt_processlist, + "db-size|dbsize" => \$::opt_dbsize, + "table-size|tablesize" => \$::opt_tablesize, + "noheaders|no-headers|n" => \$::opt_n, + "r" => \$::opt_retry, + "retries=s" => \$::opt_retries, + "debug" => \$::opt_debug); + +my $pass_through_options = (defined $::opt_p) ? join(" ",@{$::opt_p}) : ""; + +my %dburl = parse_dburl(get_alias(shift)); + +my $interactive_command; +my $batch_command; + +my $database_driver = database_driver_alias($dburl{'databasedriver'}); +if($database_driver eq "mysql" or + $database_driver eq "mysqlssl") { + ($batch_command,$interactive_command) = mysql_commands($database_driver,%dburl); +} elsif($database_driver eq "postgresql" or + $database_driver eq "postgresqlssl") { + ($batch_command,$interactive_command) = postgresql_commands($database_driver,%dburl); +} elsif($database_driver eq "oracle") { + ($batch_command,$interactive_command) = oracle_commands($database_driver,%dburl); +} + +my $err; +my $retries; +$retries ||= defined $::opt_retries ? $::opt_retries : undef; +$retries ||= defined $::opt_retry ? $::opt_retry * 3 : undef; +$retries ||= 1; + +if(defined $::opt_processlist) { + unshift @ARGV, processlist($database_driver,%dburl); +} + +if(defined $::opt_dbsize) { + unshift @ARGV, dbsize($database_driver,%dburl); +} + +if(defined $::opt_tablesize) { + unshift @ARGV, tablesize($database_driver,%dburl); +} + +do { + if(@ARGV) { + $::opt_debug and print "$batch_command\n"; + open(M,"|$batch_command") || die("mysql/psql/sqlplus not in path"); + print M "@ARGV"; + close M; + } elsif (is_stdin_terminal()) { + $::opt_debug and print "$interactive_command\n"; + system("$interactive_command"); + } else { + $::opt_debug and print "$batch_command\n"; + system("$batch_command"); + } + $err = $?>>8; +} while (--$retries and $err); + +$Global::Initfile && unlink $Global::Initfile; +exit ($err); + +sub database_driver_alias { + my $driver = shift; + my %database_driver_alias = ("mysql" => "mysql", + "mysqls" => "mysqlssl", + "mysqlssl" => "mysqlssl", + "oracle" => "oracle", + "ora" => "oracle", + "oracles" => "oraclessl", + "oras" => "oraclessl", + "oraclessl" => "oraclessl", + "orassl" => "oraclessl", + "postgresql" => "postgresql", + "pgsql" => "postgresql", + "postgres" => "postgresql", + "pg" => "postgresql", + "postgresqlssl" => "postgresqlssl", + "pgsqlssl" => "postgresqlssl", + "postgresssl" => "postgresqlssl", + "pgssl" => "postgresqlssl", + "postgresqls" => "postgresqlssl", + "pgsqls" => "postgresqlssl", + "postgress" => "postgresqlssl", + "pgs" => "postgresqlssl"); + return $database_driver_alias{$driver}; +} + +sub mysql_commands { + my ($database_driver,%opt) = (@_); + find_command_in_path("mysql") || die ("mysql not in path"); + if(defined($::opt_s)) { die "Field separator not implemented for mysql" } + my $password = defined($opt{'password'}) ? "--password=".$opt{'password'} : ""; + my $host = defined($opt{'host'}) ? "--host=".$opt{'host'} : ""; + my $port = defined($opt{'port'}) ? "--port=".$opt{'port'} : ""; + my $user = defined($opt{'user'}) ? "--user=".$opt{'user'} : ""; + my $database = defined($opt{'database'}) ? $opt{'database'} : ""; + my $html = defined($::opt_html) ? "--html" : ""; + my $no_headers = defined($::opt_n) ? "--skip-column-names" : ""; + my $ssl = ""; + if ($database_driver eq "mysqlssl") { + $ssl="--ssl"; + } + # -C: Compression if both ends support it + $batch_command = + "mysql -C $pass_through_options $no_headers $html $ssl $host $user $port $password $database"; + $interactive_command = $batch_command; + return($batch_command,$interactive_command); +} + +sub postgresql_commands { + my ($database_driver,%opt) = (@_); + find_command_in_path("psql") || die ("psql not in path"); + my $sep = ($::opt_s) ? "-A --field-separator '$::opt_s'" : ""; + my $password = defined($opt{'password'}) ? "PGPASSWORD=".$opt{'password'} : ""; + my $host = defined($opt{'host'}) ? "-h ".$opt{'host'} : ""; + my $port = defined($opt{'port'}) ? "-p ".$opt{'port'} : ""; + my $user = defined($opt{'user'}) ? "-U ".$opt{'user'} : ""; + my $database = defined($opt{'database'}) ? "-d ".$opt{'database'} : ""; + my $html = defined($::opt_html) ? "--html" : ""; + my $no_headers = defined($::opt_n) ? "--tuples-only" : ""; + my $ssl = ""; + if ($database_driver eq "postgresqlssl") { + $ssl="PGSSLMODE=require"; + } + $batch_command = + "$ssl $password psql $pass_through_options $sep $no_headers $html $host $user $port $database"; + $interactive_command = $batch_command; + + return($batch_command,$interactive_command); +} + +sub oracle_commands { + my ($database_driver,%opt) = (@_); + # oracle://user:pass@grum:1521/XE becomes: + # sqlplus 'user/pass@(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = grum)(PORT = 1521)) (CONNECT_DATA =(SERVER = DEDICATED)(SERVICE_NAME = XE) ))' + find_command_in_path("sqlplus") || die ("sqlplus not in path"); + + # Readline support: if rlwrap installed run rlwrap sqlplus + my $rlwrap = find_command_in_path("rlwrap"); + + # Set good defaults in the inifile + $Global::Initfile = "/tmp/$$.sql.init"; + open(INIT,">".$Global::Initfile) || die; + print INIT "SET LINESIZE 32767\n"; + $::opt_debug and print "SET LINESIZE 32767\n"; + print INIT "SET WRAP OFF\n"; + $::opt_debug and print "SET WRAP OFF\n"; + if(defined($::opt_html)) { + print INIT "SET MARK HTML ON\n"; + $::opt_debug and print "SET MARK HTML ON\n"; + } + if(defined($::opt_n)) { + print INIT "SET PAGES 0\n"; + $::opt_debug and print "SET PAGES 0\n"; + } else { + print INIT "SET PAGES 50000\n"; + $::opt_debug and print "SET PAGES 50000\n"; + } + if(defined($::opt_s)) { + print INIT "SET COLSEP $::opt_s\n"; + $::opt_debug and print "SET COLSEP $::opt_s\n"; + } + close INIT; + + my $password = defined($opt{'password'}) ? "/".$opt{'password'} : ""; + my $host = defined($opt{'host'}) ? $opt{'host'} : "localhost"; + my $port = defined($opt{'port'}) ? $opt{'port'} : "1521"; + my $user = defined($opt{'user'}) ? $opt{'user'} : ""; + # Database is called service in Oracle lingo + my $service = defined($opt{'database'}) ? "(SERVICE_NAME = ".$opt{'database'}.")" : ""; + my $tns = "(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = $host)(PORT = $port)) ". + "(CONNECT_DATA =(SERVER = DEDICATED)$service ))"; + my $ssl = ""; + # -L: Do not re-ask for password if it is wrong + my $common_options = "-L $pass_through_options '$user$password\@$tns' \@$Global::Initfile"; + my $batch_command = + "sqlplus -S ".$common_options; + my $interactive_command = + "$rlwrap sqlplus ".$common_options; + return($batch_command,$interactive_command); +} + + +# Return the code for 'show processlist' in the chosen database dialect +sub processlist { + my $dbdriver = shift; + my %dburl = @_; + my %processlist = + ("mysql" => "show processlist;", + "postgresql" => ("SELECT ". + " datname AS database,". + " usename AS username,". + " now()-xact_start AS runtime,". + " current_query ". + "FROM pg_stat_activity ". + "ORDER BY runtime DESC;"), + "oracle" => ("SELECT ". + ' CPU_TIME/100000, SYS.V_$SQL.SQL_TEXT, USERNAME '. + "FROM ". + ' SYS.V_$SQL, SYS.V_$SESSION '. + "WHERE ". + ' SYS.V_$SQL.SQL_ID = SYS.V_$SESSION.SQL_ID(+) '. + "AND username IS NOT NULL ". + "ORDER BY CPU_TIME DESC;"), + ); + if($processlist{$dbdriver}) { + return $processlist{$dbdriver}; + } else { + print STDERR "processlist is not implemented for $dbdriver\n"; + exit 1; + } +} + +# Return the code for 'show database size' in the chosen database dialect +sub dbsize { + my $dbdriver = shift; + my %dburl = @_; + my %processlist = + ("mysql" => ( + 'SELECT '. + ' table_schema "Data Base Name", '. + ' SUM(data_length+index_length)/1024/1024 "Data Base Size in MB" '. + 'FROM information_schema.TABLES '. + 'GROUP BY table_schema;'), + "postgresql" => ( + "SELECT ". + " pg_database_size('".$dburl{'database'}."') AS bytes,". + " pg_size_pretty(pg_database_size('".$dburl{'database'}."')) AS human_readable;"), + ); + if($processlist{$dbdriver}) { + return $processlist{$dbdriver}; + } else { + print STDERR "dbsize is not implemented for $dbdriver\n"; + exit 1; + } +} + + +# Return the code for 'show database size' in the chosen database dialect +sub tablesize { + my $dbdriver = shift; + my $database = shift; + my %processlist = + ("postgresql" => ( + "SELECT relname, relpages*8 AS kb, reltuples::int AS \"live+dead rows\" ". + "FROM pg_class c ". + "ORDER BY relpages DESC;"), + "mysql" => ( + "select table_name, TABLE_ROWS, DATA_LENGTH,INDEX_LENGTH from INFORMATION_SCHEMA.tables;"), + ); + if($processlist{$dbdriver}) { + return $processlist{$dbdriver}; + } else { + print STDERR "dbsize is not implemented for $dbdriver\n"; + exit 1; + } +} + + +sub is_stdin_terminal { + return (-t STDIN); +} + +sub find_command_in_path { + # Find the command if it exists in the current path + my $command = shift; + my $path = `which $command`; + chomp $path; + return $path; +} + +sub Usage { + if(@_) { + print "Error:\n"; + print map{ "$_\n" } @_; + print "\n"; + } + print `cat $0 | pod2man | man -l -`; +} + +sub get_alias { + my $alias = shift; + if ($alias !~ /^:/) { + return $alias; + } + # Find the alias + my $path; + if (-l $0) { + ($path) = readlink($0) =~ m|^(.*)/|; + } else { + ($path) = $0 =~ m|^(.*)/|; + } + my @urlalias=(); + check_permissions("$ENV{HOME}/.dburl.aliases"); + my @search=("$ENV{HOME}/.dburl.aliases", "$path/dburl.aliases", + "$path/dburl.aliases.dist"); + for my $alias_file (@search) { + if(-r $alias_file) { + push @urlalias, `cat "$alias_file"`; + } + } + my ($alias_part,$rest) = $alias=~/(:\w+)(.*)/; + my $dburl; + for (@urlalias) { + /^$alias_part\s+(\S+)/ and do { $dburl = $1; last; } + } + + if($dburl) { + return get_alias($dburl.$rest); + } else { + Usage("$alias is not defined in @search"); + } +} + +sub check_permissions { + my $file = shift; + + if(-e $file) { + if(not -o $file) { + my $username = (getpwuid($<))[0]; + print STDERR "$file should be owned by $username: chown $username $file\n"; + } + my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, + $atime,$mtime,$ctime,$blksize,$blocks) = stat($file); + if($mode & 077) { + my $username = (getpwuid($<))[0]; + print STDERR "$file should be only be readable by $username: chmod 600 $file\n"; + } + } +} + +sub parse_dburl { + my $url = shift; + my %options = (); + # mysql://[user[:password]@][host][:port]/[database[/jobnr]] + + if($url=~m!((?:oracle|ora|mysql|pg|postgres|postgresql)(?:s|ssl|)):// # Databasedriver ($1) + (?: + ([^:@]*) # Username ($2) + (?: + :([^@]*) # Password ($3) + )? + @)? + ([^:/]*)? # Hostname ($4) + (?: + : + ([^/]*)? # Port ($5) + )? + (?: + /+ + ([^/]*)? # Database ($6) + (?: + /+ + ([^/]*)? # Job ($7) + )? + )? + !x) { + $options{databasedriver} = undef_if_empty($1); + $options{user} = undef_if_empty($2); + $options{password} = undef_if_empty($3); + $options{host} = undef_if_empty($4); + $options{port} = undef_if_empty($5); + $options{database} = undef_if_empty($6); + $options{job} = undef_if_empty($7); + } else { + Usage("$url is not a valid DB-URL"); + exit -1; + } + return %options; +} + +sub undef_if_empty { + if(defined($_[0]) and $_[0] eq "") { + return undef; + } + return $_[0]; +} + +# TODO --list-databases: psql -l eller '\l'. mysql show databases. Oracle ?