mirror of
https://git.savannah.gnu.org/git/parallel.git
synced 2024-11-22 14:07:55 +00:00
Added: sql - a small script to access sql bases from the command line
This commit is contained in:
parent
b26dbadcc0
commit
6ad5f9f693
|
@ -64,8 +64,8 @@
|
||||||
'AM_SET_LEADING_DOT' => 1,
|
'AM_SET_LEADING_DOT' => 1,
|
||||||
'AM_SET_DEPDIR' => 1,
|
'AM_SET_DEPDIR' => 1,
|
||||||
'_AM_DEPENDENCIES' => 1,
|
'_AM_DEPENDENCIES' => 1,
|
||||||
'AM_PROG_INSTALL_SH' => 1,
|
|
||||||
'm4_include' => 1,
|
'm4_include' => 1,
|
||||||
|
'AM_PROG_INSTALL_SH' => 1,
|
||||||
'_AC_AM_CONFIG_HEADER_HOOK' => 1,
|
'_AC_AM_CONFIG_HEADER_HOOK' => 1,
|
||||||
'AU_DEFUN' => 1,
|
'AU_DEFUN' => 1,
|
||||||
'AM_MAKE_INCLUDE' => 1
|
'AM_MAKE_INCLUDE' => 1
|
||||||
|
@ -83,25 +83,25 @@
|
||||||
'configure.ac'
|
'configure.ac'
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
'_LT_AC_TAGCONFIG' => 1,
|
|
||||||
'AM_PROG_F77_C_O' => 1,
|
'AM_PROG_F77_C_O' => 1,
|
||||||
'm4_pattern_forbid' => 1,
|
'_LT_AC_TAGCONFIG' => 1,
|
||||||
'AC_INIT' => 1,
|
'AC_INIT' => 1,
|
||||||
'_AM_COND_IF' => 1,
|
'm4_pattern_forbid' => 1,
|
||||||
'AC_CANONICAL_TARGET' => 1,
|
'AC_CANONICAL_TARGET' => 1,
|
||||||
'AC_SUBST' => 1,
|
'_AM_COND_IF' => 1,
|
||||||
'AC_CONFIG_LIBOBJ_DIR' => 1,
|
'AC_CONFIG_LIBOBJ_DIR' => 1,
|
||||||
'AC_FC_SRCEXT' => 1,
|
'AC_SUBST' => 1,
|
||||||
'AC_CANONICAL_HOST' => 1,
|
'AC_CANONICAL_HOST' => 1,
|
||||||
|
'AC_FC_SRCEXT' => 1,
|
||||||
'AC_PROG_LIBTOOL' => 1,
|
'AC_PROG_LIBTOOL' => 1,
|
||||||
'AM_INIT_AUTOMAKE' => 1,
|
'AM_INIT_AUTOMAKE' => 1,
|
||||||
'AC_CONFIG_SUBDIRS' => 1,
|
'AC_CONFIG_SUBDIRS' => 1,
|
||||||
'AM_AUTOMAKE_VERSION' => 1,
|
'AM_AUTOMAKE_VERSION' => 1,
|
||||||
'LT_CONFIG_LTDL_DIR' => 1,
|
'LT_CONFIG_LTDL_DIR' => 1,
|
||||||
'AC_REQUIRE_AUX_FILE' => 1,
|
|
||||||
'AC_CONFIG_LINKS' => 1,
|
'AC_CONFIG_LINKS' => 1,
|
||||||
'm4_sinclude' => 1,
|
'AC_REQUIRE_AUX_FILE' => 1,
|
||||||
'LT_SUPPORTED_TAG' => 1,
|
'LT_SUPPORTED_TAG' => 1,
|
||||||
|
'm4_sinclude' => 1,
|
||||||
'AM_MAINTAINER_MODE' => 1,
|
'AM_MAINTAINER_MODE' => 1,
|
||||||
'AM_GNU_GETTEXT_INTL_SUBDIR' => 1,
|
'AM_GNU_GETTEXT_INTL_SUBDIR' => 1,
|
||||||
'_m4_warn' => 1,
|
'_m4_warn' => 1,
|
||||||
|
@ -114,17 +114,17 @@
|
||||||
'include' => 1,
|
'include' => 1,
|
||||||
'AM_GNU_GETTEXT' => 1,
|
'AM_GNU_GETTEXT' => 1,
|
||||||
'AC_LIBSOURCE' => 1,
|
'AC_LIBSOURCE' => 1,
|
||||||
'AM_PROG_FC_C_O' => 1,
|
|
||||||
'AC_CANONICAL_BUILD' => 1,
|
'AC_CANONICAL_BUILD' => 1,
|
||||||
|
'AM_PROG_FC_C_O' => 1,
|
||||||
'AC_FC_FREEFORM' => 1,
|
'AC_FC_FREEFORM' => 1,
|
||||||
'AH_OUTPUT' => 1,
|
'AH_OUTPUT' => 1,
|
||||||
'_AM_SUBST_NOTMAKE' => 1,
|
|
||||||
'AC_CONFIG_AUX_DIR' => 1,
|
'AC_CONFIG_AUX_DIR' => 1,
|
||||||
'sinclude' => 1,
|
'_AM_SUBST_NOTMAKE' => 1,
|
||||||
'm4_pattern_allow' => 1,
|
|
||||||
'AM_PROG_CC_C_O' => 1,
|
'AM_PROG_CC_C_O' => 1,
|
||||||
'AC_CANONICAL_SYSTEM' => 1,
|
'm4_pattern_allow' => 1,
|
||||||
|
'sinclude' => 1,
|
||||||
'AM_CONDITIONAL' => 1,
|
'AM_CONDITIONAL' => 1,
|
||||||
|
'AC_CANONICAL_SYSTEM' => 1,
|
||||||
'AC_CONFIG_HEADERS' => 1,
|
'AC_CONFIG_HEADERS' => 1,
|
||||||
'AC_DEFINE_TRACE_LITERAL' => 1,
|
'AC_DEFINE_TRACE_LITERAL' => 1,
|
||||||
'm4_include' => 1,
|
'm4_include' => 1,
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
Bugfix: --eta/--progress with 0 jobs gave division by zero.
|
|
||||||
Bugfix in Makefile by Piotr Jaroszyński <p dot jaroszynski at gmail dot com>
|
|
||||||
|
|
||||||
fex syntax for splitting fields
|
fex syntax for splitting fields
|
||||||
http://www.semicomplete.com/projects/fex/
|
http://www.semicomplete.com/projects/fex/
|
||||||
sql :foo 'select * from bar' | parallel --fex '|{1,2}' do_stuff {2} {1}
|
sql :foo 'select * from bar' | parallel --fex '|{1,2}' do_stuff {2} {1}
|
||||||
|
|
||||||
Import sql
|
Import sql
|
||||||
|
|
||||||
inputfile tabel, Split colonner til {n}
|
|
||||||
sql :foo 'select * from bar' | parallel --colsep '\|' do_stuff {4} {1}
|
sql :foo 'select * from bar' | parallel --colsep '\|' do_stuff {4} {1}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ to:parallel@gnu.org, bug-parallel@gnu.org, info-gnu@gnu.org, bug-directory@gnu.o
|
||||||
cc:Peter Simons <simons@cryp.to>, Sandro Cazzaniga <kharec@mandriva.org>,
|
cc:Peter Simons <simons@cryp.to>, Sandro Cazzaniga <kharec@mandriva.org>,
|
||||||
Tim Cuthbertson <tim3d.junk@gmail.com>, Ludovic Courtès <ludo@gnu.org>,
|
Tim Cuthbertson <tim3d.junk@gmail.com>, Ludovic Courtès <ludo@gnu.org>,
|
||||||
Markus Ammer <mkmm@gmx-topmail.de>, Pavel Nuzhdin <pnzhdin@gmail.com>,
|
Markus Ammer <mkmm@gmx-topmail.de>, Pavel Nuzhdin <pnzhdin@gmail.com>,
|
||||||
Phil Sung <psung@alum.mit.edu>
|
Phil Sung <psung@alum.mit.edu>, Michael Shigorin <mike@altlinux.org>
|
||||||
|
|
||||||
Subject: GNU Parallel 20100822 released
|
Subject: GNU Parallel 20100822 released
|
||||||
|
|
||||||
|
@ -99,7 +99,9 @@ download at: http://ftp.gnu.org/gnu/parallel/
|
||||||
New in this release:
|
New in this release:
|
||||||
|
|
||||||
* First community generated bugfixes
|
* First community generated bugfixes
|
||||||
Piotr Jaroszyński <p.jaroszynski@gmail.com>
|
|
||||||
|
* Alt Linux package of GNU Parallel. Thanks to Michael Shigorin <mike
|
||||||
|
at altlinux dot org>
|
||||||
|
|
||||||
= About GNU Parallel =
|
= About GNU Parallel =
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
bin_SCRIPTS = parallel sem
|
bin_SCRIPTS = parallel sem sql
|
||||||
man_MANS = parallel.1 sem.1
|
man_MANS = parallel.1 sem.1 sql.1
|
||||||
doc_DATA = parallel.html sem.html
|
doc_DATA = parallel.html sem.html sql.html
|
||||||
|
|
||||||
parallel.1: parallel Makefile
|
parallel.1: parallel Makefile
|
||||||
pod2man --release='$(PACKAGE_VERSION)' --center='$(PACKAGE_NAME)' \
|
pod2man --release='$(PACKAGE_VERSION)' --center='$(PACKAGE_NAME)' \
|
||||||
|
@ -10,6 +10,10 @@ sem.1: sem.pod Makefile
|
||||||
pod2man --release='$(PACKAGE_VERSION)' --center='$(PACKAGE_NAME)' \
|
pod2man --release='$(PACKAGE_VERSION)' --center='$(PACKAGE_NAME)' \
|
||||||
--section=1 $(srcdir)/sem.pod > $(srcdir)/sem.1
|
--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
|
parallel.html: parallel Makefile
|
||||||
pod2html $(srcdir)/parallel > $(srcdir)/parallel.html
|
pod2html $(srcdir)/parallel > $(srcdir)/parallel.html
|
||||||
rm -f $(srcdir)/pod2htm*
|
rm -f $(srcdir)/pod2htm*
|
||||||
|
@ -18,8 +22,12 @@ sem.html: sem.pod Makefile
|
||||||
pod2html $(srcdir)/sem.pod > $(srcdir)/sem.html
|
pod2html $(srcdir)/sem.pod > $(srcdir)/sem.html
|
||||||
rm -f $(srcdir)/pod2htm*
|
rm -f $(srcdir)/pod2htm*
|
||||||
|
|
||||||
|
sql.html: sql Makefile
|
||||||
|
pod2html $(srcdir)/sql > $(srcdir)/sql.html
|
||||||
|
rm -f $(srcdir)/pod2htm*
|
||||||
|
|
||||||
sem: parallel
|
sem: parallel
|
||||||
ln -fs parallel sem
|
ln -fs parallel sem
|
||||||
|
|
||||||
DISTCLEANFILES = parallel.1 sem.1 parallel.html sem.html
|
DISTCLEANFILES = parallel.1 sem.1 sql.1 parallel.html sem.html sql.html
|
||||||
EXTRA_DIST = parallel sem parallel.1 sem.1 parallel.html sem.html sem.pod
|
EXTRA_DIST = parallel sem sql parallel.1 sem.1 sql.1 parallel.html sem.html sem.pod sql.html
|
||||||
|
|
|
@ -146,11 +146,11 @@ target_alias = @target_alias@
|
||||||
top_build_prefix = @top_build_prefix@
|
top_build_prefix = @top_build_prefix@
|
||||||
top_builddir = @top_builddir@
|
top_builddir = @top_builddir@
|
||||||
top_srcdir = @top_srcdir@
|
top_srcdir = @top_srcdir@
|
||||||
bin_SCRIPTS = parallel sem
|
bin_SCRIPTS = parallel sem sql
|
||||||
man_MANS = parallel.1 sem.1
|
man_MANS = parallel.1 sem.1 sql.1
|
||||||
doc_DATA = parallel.html sem.html
|
doc_DATA = parallel.html sem.html sql.html
|
||||||
DISTCLEANFILES = parallel.1 sem.1 parallel.html sem.html
|
DISTCLEANFILES = parallel.1 sem.1 sql.1 parallel.html sem.html sql.html
|
||||||
EXTRA_DIST = parallel sem parallel.1 sem.1 parallel.html sem.html sem.pod
|
EXTRA_DIST = parallel sem sql parallel.1 sem.1 sql.1 parallel.html sem.html sem.pod sql.html
|
||||||
all: all-am
|
all: all-am
|
||||||
|
|
||||||
.SUFFIXES:
|
.SUFFIXES:
|
||||||
|
@ -451,6 +451,10 @@ sem.1: sem.pod Makefile
|
||||||
pod2man --release='$(PACKAGE_VERSION)' --center='$(PACKAGE_NAME)' \
|
pod2man --release='$(PACKAGE_VERSION)' --center='$(PACKAGE_NAME)' \
|
||||||
--section=1 $(srcdir)/sem.pod > $(srcdir)/sem.1
|
--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
|
parallel.html: parallel Makefile
|
||||||
pod2html $(srcdir)/parallel > $(srcdir)/parallel.html
|
pod2html $(srcdir)/parallel > $(srcdir)/parallel.html
|
||||||
rm -f $(srcdir)/pod2htm*
|
rm -f $(srcdir)/pod2htm*
|
||||||
|
@ -459,6 +463,10 @@ sem.html: sem.pod Makefile
|
||||||
pod2html $(srcdir)/sem.pod > $(srcdir)/sem.html
|
pod2html $(srcdir)/sem.pod > $(srcdir)/sem.html
|
||||||
rm -f $(srcdir)/pod2htm*
|
rm -f $(srcdir)/pod2htm*
|
||||||
|
|
||||||
|
sql.html: sql Makefile
|
||||||
|
pod2html $(srcdir)/sql > $(srcdir)/sql.html
|
||||||
|
rm -f $(srcdir)/pod2htm*
|
||||||
|
|
||||||
sem: parallel
|
sem: parallel
|
||||||
ln -fs parallel sem
|
ln -fs parallel sem
|
||||||
|
|
||||||
|
|
697
src/sql
Executable file
697
src/sql
Executable file
|
@ -0,0 +1,697 @@
|
||||||
|
#!/usr/bin/perl -w
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
sql - execute a command on a database determined by a dburl
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
B<sql> [B<-hnr>] [B<--table-size>] [B<--db-size>] [B<-p> I<pass-through>] [B<-s> I<string>] I<dburl> [I<command>]
|
||||||
|
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
GNU B<sql> 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<command> is left out you
|
||||||
|
will get that database's interactive shell.
|
||||||
|
|
||||||
|
GNU B<sql> is often used in combination with GNU B<parallel>.
|
||||||
|
|
||||||
|
=over 9
|
||||||
|
|
||||||
|
=item I<dburl>
|
||||||
|
|
||||||
|
A DBURL has the following syntax:
|
||||||
|
vendor://[user[:password]@][host][:port]/[database]
|
||||||
|
|
||||||
|
See the section DBURL below.
|
||||||
|
|
||||||
|
=item I<command>
|
||||||
|
|
||||||
|
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<sql> 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<pass-through>
|
||||||
|
|
||||||
|
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<ntimes>
|
||||||
|
|
||||||
|
Try I<ntimes> times. If the client program returns with an error,
|
||||||
|
retry the command. Default is I<--retries 1>.
|
||||||
|
|
||||||
|
|
||||||
|
=item B<--sep> I<string>
|
||||||
|
|
||||||
|
=item B<-s> I<string>
|
||||||
|
|
||||||
|
Field separator. Use I<string> 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<sql> 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<sql> is part of GNU B<parallel>. Report bugs to <bug-parallel@gnu.org>.
|
||||||
|
|
||||||
|
|
||||||
|
=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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
=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 Share>
|
||||||
|
|
||||||
|
to copy, distribute and transmit the work
|
||||||
|
|
||||||
|
=item B<to Remix>
|
||||||
|
|
||||||
|
to adapt the work
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
Under the following conditions:
|
||||||
|
|
||||||
|
=over 9
|
||||||
|
|
||||||
|
=item B<Attribution>
|
||||||
|
|
||||||
|
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<Share Alike>
|
||||||
|
|
||||||
|
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<Waiver>
|
||||||
|
|
||||||
|
Any of the above conditions can be waived if you get permission from
|
||||||
|
the copyright holder.
|
||||||
|
|
||||||
|
=item B<Public Domain>
|
||||||
|
|
||||||
|
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<Other Rights>
|
||||||
|
|
||||||
|
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<Notice>
|
||||||
|
|
||||||
|
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<sql> uses Perl. If B<mysql> is installed, MySQL dburls will
|
||||||
|
work. If B<psql> is installed, PostgreSQL dburls will work. If
|
||||||
|
B<sqlplus> is installed, Oracle dburls will work. If B<rlwrap> is
|
||||||
|
installed, GNU B<sql> 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<mysql>(1), B<psql>(1), B<sqlplus>(1), B<rlwrap>(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 ?
|
Loading…
Reference in a new issue