3 # Copyright (C) 2008-2010 Ole Tange, http://ole.tange.dk
5 # Copyright (C) 2010-2022 Ole Tange, http://ole.tange.dk and
6 # Free Software Foundation, Inc.
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, see <http://www.gnu.org/licenses/>
20 # or write to the Free Software Foundation, Inc., 51 Franklin St,
21 # Fifth Floor, Boston, MA 02110-1301 USA
23 # SPDX-FileCopyrightText: 2008-2022 Ole Tange, http://ole.tange.dk and Free Software and Foundation, Inc.
24 # SPDX-License-Identifier: GPL-3.0-or-later
25 # SPDX-License-Identifier: GFDL-1.3-or-later
29 sql - execute a command on a database determined by a dburl
33 B<sql> [options] I<dburl> [I<commands>]
35 B<sql> [options] I<dburl> < commandfile
37 B<#!/usr/bin/sql> B<--shebang> [options] I<dburl>
41 GNU B<sql> aims to give a simple, unified interface for accessing
42 databases through all the different databases' command line
43 clients. So far the focus has been on giving a common way to specify
44 login information (protocol, username, password, hostname, and port
45 number), size (database and table size), and running queries.
47 The database is addressed using a DBURL. If I<commands> are left out
48 you will get that database's interactive shell.
50 GNU B<sql> is often used in combination with GNU B<parallel>.
56 A DBURL has the following syntax:
58 [[user][:password]@][host][:port]/[database][?sqlquery]
60 See the section DBURL below.
64 The SQL commands to run. Each argument will have a newline
67 Example: "SELECT * FROM foo;" "SELECT * FROM bar;"
69 If the arguments contain '\n' or '\x0a' this will be replaced with a
72 Example: "SELECT * FROM foo;\n SELECT * FROM bar;"
74 If no commands are given SQL is read from the keyboard or STDIN.
76 Example: echo 'SELECT * FROM foo;' | sql mysql:///
79 =item B<--csv> (beta testing)
88 Size of database. Show the size of the database on disk. For Oracle
89 this requires access to read the table I<dba_data_files> - the user
97 Print a summary of the options to GNU B<sql> and exit.
102 HTML output. Turn on HTML tabular output.
105 =item B<--json> (beta testing)
107 =item B<--pretty> (beta testing)
112 =item B<--list-databases>
116 =item B<--show-databases>
120 List the databases (table spaces) in the database.
127 =item B<--show-processlist>
129 Show the list of running queries.
132 =item B<--list-tables>
134 =item B<--show-tables>
136 =item B<--table-list>
138 List the tables in the database.
143 =item B<--no-headers>
147 Remove headers and footers and print only tuples. Bug in Oracle: it
148 still prints number of rows found.
151 =item B<-p> I<pass-through>
153 The string following -p will be given to the database connection
154 program as arguments. Multiple -p's will be joined with
155 space. Example: pass '-U' and the user name to the program:
157 I<-p "-U scott"> can also be written I<-p -U -p scott>.
160 =item B<--precision> <I<rfc3339|h|m|s|ms|u|ns>>
162 Precision of timestamps.
164 Specifiy the format of the output timestamps: rfc3339, h, m, s, ms, u
170 Try 3 times. Short version of I<--retries 3>.
173 =item B<--retries> I<ntimes>
175 Try I<ntimes> times. If the client program returns with an error,
176 retry the command. Default is I<--retries 1>.
179 =item B<--sep> I<string>
181 =item B<-s> I<string>
183 Field separator. Use I<string> as separator between columns.
186 =item B<--skip-first-line>
188 Do not use the first line of input (used by GNU B<sql> itself
189 when called with B<--shebang>).
192 =item B<--table-size>
196 Size of tables. Show the size of the tables in the database.
203 Print which command is sent.
210 Print the version GNU B<sql> and exit.
217 GNU B<sql> can be called as a shebang (#!) command as the first line of a script. Like this:
219 #!/usr/bin/sql -Y mysql:///
223 For this to work B<--shebang> or B<-Y> must be set as the first option.
229 A DBURL has the following syntax:
231 [[user][:password]@][host][:port]/[database][?sqlquery]
233 To quote special characters use %-encoding specified in
234 http://tools.ietf.org/html/rfc3986#section-2.1 (E.g. a password
235 containing '/' would contain '%2F').
239 mysql://scott:tiger@my.example.com/mydb
240 influxdb://scott:tiger@influxdb.example.com/foo
241 sql:oracle://scott:tiger@ora.example.com/xe
242 postgresql://scott:tiger@pg.example.com/pgdb
244 postgresqlssl://scott@pg.example.com:3333/pgdb
245 sql:sqlite2:////tmp/db.sqlite?SELECT * FROM foo;
246 sqlite3:///../db.sqlite3?SELECT%20*%20FROM%20foo;
248 Currently supported vendors: MySQL (mysql), MySQL with SSL (mysqls,
249 mysqlssl), Oracle (oracle, ora), PostgreSQL (postgresql, pg, pgsql,
250 postgres), PostgreSQL with SSL (postgresqlssl, pgs, pgsqlssl,
251 postgresssl, pgssl, postgresqls, pgsqls, postgress), SQLite2 (sqlite,
252 sqlite2), SQLite3 (sqlite3), InfluxDB 1.x (influx, influxdb), InfluxDB
253 with SSL (influxdbssl, influxdbs, influxs, influxssl)
255 Aliases must start with ':' and are read from
256 /etc/sql/aliases and ~/.sql/aliases. The user's own
257 ~/.sql/aliases should only be readable by the user.
261 :myalias1 pg://scott:tiger@pg.example.com/pgdb
262 :myalias2 ora://scott:tiger@ora.example.com/xe
263 # Short form of mysql://`whoami`:nopassword@localhost:3306/`whoami`
265 # Short form of mysql://`whoami`:nopassword@localhost:33333/mydb
266 :myalias4 mysql://:33333/mydb
269 # the sortest alias possible
270 : sqlite2:////tmp/db.sqlite
271 # Including an SQL query
272 :query sqlite:////tmp/db.sqlite?SELECT * FROM foo;
276 =head2 Get an interactive prompt
278 The most basic use of GNU B<sql> is to get an interactive prompt:
280 B<sql sql:oracle://scott:tiger@ora.example.com/xe>
282 If you have setup an alias you can do:
289 To run a query directly from the command line:
291 B<sql :myalias "SELECT * FROM foo;">
293 Oracle requires newlines after each statement. This can be done like
296 B<sql :myora "SELECT * FROM foo;" "SELECT * FROM bar;">
300 B<sql :myora "SELECT * FROM foo;\nSELECT * FROM bar;">
303 =head2 Copy a PostgreSQL database
305 To copy a PostgreSQL database use pg_dump to generate the dump and GNU
308 B<pg_dump pg_database | sql pg://scott:tiger@pg.example.com/pgdb>
311 =head2 Empty all tables in a MySQL database
313 Using GNU B<parallel> it is easy to empty all tables without dropping them:
315 B<sql -n mysql:/// 'show tables' | parallel sql mysql:/// DELETE FROM {};>
318 =head2 Drop all tables in a PostgreSQL database
320 To drop all tables in a PostgreSQL database do:
322 B<sql -n pg:/// '\dt' | parallel --colsep '\|' -r sql pg:/// DROP TABLE {2};>
325 =head2 Run as a script
329 B<sql mysql:/// < sqlfile>
331 you can combine the sqlfile with the DBURL to make a
332 UNIX-script. Create a script called I<demosql>:
334 B<#!/usr/bin/sql -Y mysql:///>
336 B<SELECT * FROM foo;>
340 B<chmod +x demosql; ./demosql>
343 =head2 Use --colsep to process multiple columns
345 Use GNU B<parallel>'s B<--colsep> to separate columns:
347 B<sql -s '\t' :myalias 'SELECT * FROM foo;' | parallel --colsep '\t' do_stuff {4} {1}>
350 =head2 Retry if the connection fails
352 If the access to the database fails occasionally B<--retries> can help
353 make sure the query succeeds:
355 B<sql --retries 5 :myalias 'SELECT * FROM really_big_foo;'>
358 =head2 Get info about the running database system
360 Show how big the database is:
362 B<sql --db-size :myalias>
366 B<sql --list-tables :myalias>
368 List the size of the tables:
370 B<sql --table-size :myalias>
372 List the running processes:
374 B<sql --show-processlist :myalias>
377 =head1 REPORTING BUGS
379 GNU B<sql> is part of GNU B<parallel>. Report bugs to <bug-parallel@gnu.org>.
384 When using GNU B<sql> for a publication please cite:
386 O. Tange (2011): GNU SQL - A Command Line Tool for Accessing Different
387 Databases Using DBURLs, ;login: The USENIX Magazine, April 2011:29-32.
389 Copyright (C) 2008-2010 Ole Tange http://ole.tange.dk
391 Copyright (C) 2010-2022 Ole Tange, http://ole.tange.dk and Free
392 Software Foundation, Inc.
396 This program is free software; you can redistribute it and/or modify
397 it under the terms of the GNU General Public License as published by
398 the Free Software Foundation; either version 3 of the License, or
399 at your option any later version.
401 This program is distributed in the hope that it will be useful,
402 but WITHOUT ANY WARRANTY; without even the implied warranty of
403 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
404 GNU General Public License for more details.
406 You should have received a copy of the GNU General Public License
407 along with this program. If not, see <http://www.gnu.org/licenses/>.
409 =head2 Documentation license I
411 Permission is granted to copy, distribute and/or modify this
412 documentation under the terms of the GNU Free Documentation License,
413 Version 1.3 or any later version published by the Free Software
414 Foundation; with no Invariant Sections, with no Front-Cover Texts, and
415 with no Back-Cover Texts. A copy of the license is included in the
416 file LICENSES/GFDL-1.3-or-later.txt.
419 =head2 Documentation license II
427 to copy, distribute and transmit the work
435 Under the following conditions:
441 You must attribute the work in the manner specified by the author or
442 licensor (but not in any way that suggests that they endorse you or
443 your use of the work).
447 If you alter, transform, or build upon this work, you may distribute
448 the resulting work only under the same, similar or a compatible
453 With the understanding that:
459 Any of the above conditions can be waived if you get permission from
460 the copyright holder.
462 =item B<Public Domain>
464 Where the work or any of its elements is in the public domain under
465 applicable law, that status is in no way affected by the license.
467 =item B<Other Rights>
469 In no way are any of the following rights affected by the license:
475 Your fair dealing or fair use rights, or other applicable
476 copyright exceptions and limitations;
480 The author's moral rights;
484 Rights other persons may have either in the work itself or in
485 how the work is used, such as publicity or privacy rights.
491 For any reuse or distribution, you must make clear to others the
492 license terms of this work.
496 A copy of the full license is included in the file as cc-by-sa.txt.
500 GNU B<sql> uses Perl. If B<mysql> is installed, MySQL dburls will
501 work. If B<psql> is installed, PostgreSQL dburls will work. If
502 B<sqlite> is installed, SQLite2 dburls will work. If B<sqlite3> is
503 installed, SQLite3 dburls will work. If B<sqlplus> is installed,
504 Oracle dburls will work. If B<rlwrap> is installed, GNU B<sql> will
505 have a command history for Oracle.
510 ~/.sql/aliases - user's own aliases with DBURLs
512 /etc/sql/aliases - common aliases with DBURLs
517 B<mysql>(1), B<psql>(1), B<rlwrap>(1), B<sqlite>(1), B<sqlite3>(1),
518 B<sqlplus>(1), B<influx>(1)
524 use File
::Temp qw
/tempfile tempdir/;
528 my $pass_through_options = (defined $::opt_p
) ?
join(" ",@
{$::opt_p
}) : "";
529 my $dburl_or_alias = shift;
530 if (not defined $dburl_or_alias) { Usage
("No DBURL given"); exit -1; }
531 my %dburl = parse_dburl
(get_alias
($dburl_or_alias));
533 my $interactive_command;
536 my $database_driver = database_driver_alias
($dburl{'databasedriver'});
537 if($database_driver eq "mysql" or
538 $database_driver eq "mysqlssl") {
539 ($batch_command,$interactive_command) =
540 mysql_commands
($database_driver,%dburl);
541 } elsif($database_driver eq "postgresql" or
542 $database_driver eq "postgresqlssl") {
543 ($batch_command,$interactive_command) =
544 postgresql_commands
($database_driver,%dburl);
545 } elsif($database_driver eq "oracle") {
546 ($batch_command,$interactive_command) =
547 oracle_commands
($database_driver,%dburl);
548 } elsif($database_driver eq "sqlite" or
549 $database_driver eq "sqlite3") {
550 ($batch_command,$interactive_command) =
551 sqlite_commands
($database_driver,%dburl);
552 } elsif($database_driver eq "influx" or
553 $database_driver eq "influxssl") {
554 ($batch_command,$interactive_command) =
555 influx_commands
($database_driver,%dburl);
560 $retries ||= defined $::opt_retries ?
$::opt_retries
: undef;
561 $retries ||= defined $::opt_retry ?
$::opt_retry
* 3 : undef;
564 if(defined $::opt_processlist
) {
565 unshift @ARGV, processlist
($database_driver,%dburl);
568 if(defined $::opt_tablelist
) {
569 unshift @ARGV, tablelist
($database_driver,%dburl);
572 if(defined $::opt_dblist
) {
573 unshift @ARGV, dblist
($database_driver,%dburl);
576 if(defined $::opt_dbsize
) {
577 unshift @ARGV, dbsize
($database_driver,%dburl);
580 if(defined $::opt_tablesize
) {
581 unshift @ARGV, tablesize
($database_driver,%dburl);
585 if($dburl{'query'}) {
587 ($fh,$queryfile) = tempfile
(SUFFIX
=> ".sql");
588 print $fh $dburl{'query'},"\n";
590 $batch_command = "(cat $queryfile;rm $queryfile; cat) | $batch_command";
594 # Quote for other shells (Bourne compatibles)
596 # $string = string to be quoted
598 # $shell_quoted = string quoted as needed by the shell
600 if($s =~ /[^-_.+a-z0-9\/]/i
) {
601 $s =~ s/'/'"'"'/g; # "-quote single quotes
602 $s = "'$s'"; # '-quote entire string
603 $s =~ s/^''//; # Remove unneeded '' at ends
604 $s =~ s/''$//; # (faster than s/^''|''$//g)
616 # SQL Commands given as arguments:
618 $::opt_debug
and print "[ | $batch_command]\n";
619 $::opt_verbose
and print "[ | $batch_command]\n";
620 if($database_driver eq "influx" or $database_driver eq "influxssl") {
621 # Influx currently cannot read commands from stdin
625 $::opt_debug
and print "[$batch_command -execute $_]\n";
626 system("$batch_command -execute ".shell_quote
($_));
629 open(M
,"| $batch_command") ||
630 die("mysql/psql/sqlplus/influx not in path");
639 if (is_stdin_terminal
()) {
641 $::opt_debug
and print "[$interactive_command]\n";
642 $::opt_verbose
and print "[$interactive_command]\n";
643 system("$interactive_command");
645 # Let the command read from stdin
646 $::opt_debug
and print "[$batch_command]\n";
647 $::opt_verbose
and print "[$batch_command]\n";
648 if($database_driver eq "influx" or $database_driver eq "influxssl") {
649 # Influx currently cannot read commands from stdin
653 $::opt_debug
and print "[$batch_command -execute $_]\n";
654 system("$batch_command -execute ".shell_quote
($_));
657 system("$batch_command");
662 } while (--$retries and $err);
664 $queryfile and unlink $queryfile;
666 $Global::Initfile
&& unlink $Global::Initfile
;
670 $Global::version
= 20221022;
671 $Global::progname
= 'sql';
673 # This must be done first as this may exec myself
674 if(defined $ARGV[0] and ($ARGV[0]=~/^-Y/ or $ARGV[0]=~/^--shebang /)) {
675 # Program is called from #! line in script
676 $ARGV[0]=~s/^-Y //; # remove -Y if on its own
677 $ARGV[0]=~s/^-Y/-/; # remove -Y if bundled with other options
678 $ARGV[0]=~s/^--shebang //; # remove --shebang if it is set
679 my $argfile = pop @ARGV;
680 # exec myself to split @ARGV into separate fields
681 exec "$0 --skip-first-line < $argfile @ARGV";
683 Getopt
::Long
::Configure
("bundling","require_order");
684 GetOptions
("passthrough|p=s@" => \
$::opt_p
,
685 "sep|s=s" => \
$::opt_s
,
686 "html" => \
$::opt_html
,
687 "show-processlist|proclist|listproc|showqueries|show-queries"
688 => \
$::opt_processlist
,
689 "show-tables|showtables|listtables|list-tables|".
690 "tablelist|table-list|show-measurements|showmeasurements|".
691 "list-measurements|listmeasurements" => \
$::opt_tablelist
,
693 "listdb|listdbs|list-db|list-dbs|list-database|".
694 "list-databases|listdatabases|listdatabase|showdb|".
695 "showdbs|show-db|show-dbs|show-database|show-databases|".
696 "showdatabases|showdatabase" => \
$::opt_dblist
,
697 "db-size|dbsize" => \
$::opt_dbsize
,
698 "table-size|tablesize" => \
$::opt_tablesize
,
699 "json|j" => \
$::opt_json
,
700 "pretty" => \
$::opt_pretty
,
701 "csv" => \
$::opt_csv
,
702 "precision=s" => \
$::opt_precision
,
703 "noheaders|no-headers|n" => \
$::opt_n
,
704 "r" => \
$::opt_retry
,
705 "retries=s" => \
$::opt_retries
,
706 "debug|D" => \
$::opt_debug
,
707 # Shebang #!/usr/bin/parallel -Yotheroptions
708 "Y|shebang" => \
$::opt_shebang
,
709 "skip-first-line" => \
$::opt_skip_first_line
,
711 "help|h" => \
$::opt_help
,
712 "version|V" => \
$::opt_version
,
713 "verbose|v" => \
$::opt_verbose
,
716 if(defined $::opt_help
) { die_usage
(); }
717 if(defined $::opt_version
) { version
(); exit(0); }
718 $Global::debug
= $::opt_debug
;
721 sub database_driver_alias
{
723 my %database_driver_alias = ("mysql" => "mysql",
724 "mysqls" => "mysqlssl",
725 "mysqlssl" => "mysqlssl",
726 "oracle" => "oracle",
728 "oracles" => "oraclessl",
729 "oras" => "oraclessl",
730 "oraclessl" => "oraclessl",
731 "orassl" => "oraclessl",
732 "postgresql" => "postgresql",
733 "pgsql" => "postgresql",
734 "postgres" => "postgresql",
735 "pg" => "postgresql",
736 "postgresqlssl" => "postgresqlssl",
737 "pgsqlssl" => "postgresqlssl",
738 "postgresssl" => "postgresqlssl",
739 "pgssl" => "postgresqlssl",
740 "postgresqls" => "postgresqlssl",
741 "pgsqls" => "postgresqlssl",
742 "postgress" => "postgresqlssl",
743 "pgs" => "postgresqlssl",
744 "sqlite" => "sqlite",
745 "sqlite2" => "sqlite",
746 "sqlite3" => "sqlite3",
747 "influx" => "influx",
748 "influxdb" => "influx",
749 "influxssl" => "influxssl",
750 "influxdbssl" => "influxssl",
751 "influxs" => "influxssl",
752 "influxdbs" => "influxssl",
754 return $database_driver_alias{$driver};
758 my ($database_driver,%opt) = (@_);
759 find_command_in_path
("mysql") || die ("mysql not in path");
760 if(defined($::opt_s
)) { die "Field separator not implemented for mysql" }
761 my $password = defined($opt{'password'}) ?
"--password=".$opt{'password'} : "";
762 my $host = defined($opt{'host'}) ?
"--host=".$opt{'host'} : "";
763 my $port = defined($opt{'port'}) ?
"--port=".$opt{'port'} : "";
764 my $user = defined($opt{'user'}) ?
"--user=".$opt{'user'} : "";
765 my $database = defined($opt{'database'}) ?
$opt{'database'} : $ENV{'USER'};
766 my $html = defined($::opt_html
) ?
"--html" : "";
767 my $no_headers = defined($::opt_n
) ?
"--skip-column-names" : "";
769 if ($database_driver eq "mysqlssl") { $ssl="--ssl"; }
770 my($credential_fh,$tmp) = tempfile
(SUFFIX
=> ".sql");
772 print $credential_fh ("[client]\n",
773 $user && "user=$opt{'user'}\n",
774 $password && "password=$opt{'password'}\n",
775 $host && "host=$opt{'host'}\n");
776 close $credential_fh;
778 # Prepend with a remover of the credential tempfile
779 # -C: Compression if both ends support it
781 "((sleep 1; rm $tmp) & ".
782 "mysql --defaults-extra-file=$tmp -C $pass_through_options ".
783 "$no_headers $html $ssl $host $user $port $database)";
784 $interactive_command = $batch_command;
785 return($batch_command,$interactive_command);
788 sub postgresql_commands
{
789 my ($database_driver,%opt) = (@_);
790 find_command_in_path
("psql") || die ("psql not in path");
791 my $sep = ($::opt_s
) ?
"-A --field-separator '$::opt_s'" : "";
792 my $password = defined($opt{'password'}) ?
793 "PGPASSWORD=".$opt{'password'} : "";
794 my $host = defined($opt{'host'}) ?
"-h ".$opt{'host'} : "";
795 my $port = defined($opt{'port'}) ?
"-p ".$opt{'port'} : "";
796 my $user = defined($opt{'user'}) ?
"-U ".$opt{'user'} : "";
797 my $database = defined($opt{'database'}) ?
"-d ".$opt{'database'} : "";
798 my $html = defined($::opt_html
) ?
"--html" : "";
799 my $no_headers = defined($::opt_n
) ?
"--tuples-only" : "";
801 if ($database_driver eq "postgresqlssl") { $ssl="PGSSLMODE=require"; }
803 "$ssl $password psql $pass_through_options $sep $no_headers ".
804 "$html $host $user $port $database";
805 $interactive_command = $batch_command;
807 return($batch_command,$interactive_command);
810 sub oracle_commands
{
811 my ($database_driver,%opt) = (@_);
812 # oracle://user:pass@grum:1521/XE becomes:
813 # sqlplus 'user/pass@(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = grum)(PORT = 1521)) (CONNECT_DATA =(SERVER = DEDICATED)(SERVICE_NAME = XE) ))'
814 my $sqlplus = find_command_in_path
("sqlplus") ||
815 find_command_in_path
("sqlplus64") or
816 die("sqlplus/sqlplus64 not in path");
818 # Readline support: if rlwrap installed run rlwrap sqlplus
819 my $rlwrap = find_command_in_path
("rlwrap");
821 # Set good defaults in the inifile
822 $Global::Initfile
= "/tmp/$$.sql.init";
823 open(INIT,">".$Global::Initfile
) || die;
824 print INIT "SET LINESIZE 32767\n";
825 $::opt_debug
and print "SET LINESIZE 32767\n";
826 print INIT "SET WRAP OFF\n";
827 $::opt_debug
and print "SET WRAP OFF\n";
828 if(defined($::opt_html
)) {
829 print INIT "SET MARK HTML ON\n";
830 $::opt_debug
and print "SET MARK HTML ON\n";
832 if(defined($::opt_n
)) {
833 print INIT "SET PAGES 0\n";
834 $::opt_debug
and print "SET PAGES 0\n";
836 print INIT "SET PAGES 50000\n";
837 $::opt_debug
and print "SET PAGES 50000\n";
839 if(defined($::opt_s
)) {
840 print INIT "SET COLSEP $::opt_s\n";
841 $::opt_debug
and print "SET COLSEP $::opt_s\n";
845 my $password = defined($opt{'password'}) ?
"/".$opt{'password'} : "";
846 my $host = defined($opt{'host'}) ?
$opt{'host'} : "localhost";
847 my $port = defined($opt{'port'}) ?
$opt{'port'} : "1521";
848 my $user = defined($opt{'user'}) ?
$opt{'user'} : "";
849 # Database is called service in Oracle lingo
850 my $service = defined($opt{'database'}) ?
"(SERVICE_NAME = ".$opt{'database'}.")" : "";
851 my $tns = "(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = $host)(PORT = $port)) ".
852 "(CONNECT_DATA =(SERVER = DEDICATED)$service ))";
854 # -L: Do not re-ask for password if it is wrong
855 my $common_options = "-L $pass_through_options ".
856 "'$user$password\@$tns' \@$Global::Initfile";
857 my $batch_command = "$sqlplus -S ".$common_options;
858 my $interactive_command = "$rlwrap $sqlplus ".$common_options;
859 return($batch_command,$interactive_command);
862 sub sqlite_commands
{
863 my ($database_driver,%opt) = (@_);
864 if(not find_command_in_path
($database_driver)) {
865 print STDERR
"Database driver '$database_driver' not supported\n";
868 my $sep = defined($::opt_s
) ?
"-separator '$::opt_s'" : "";
869 my $password = defined($opt{'password'}) ?
"--password=".$opt{'password'} : "";
870 my $host = defined($opt{'host'}) ?
"--host=".$opt{'host'} : "";
871 my $port = defined($opt{'port'}) ?
"--port=".$opt{'port'} : "";
872 my $user = defined($opt{'user'}) ?
"--user=".$opt{'user'} : "";
873 my $database = defined($opt{'database'}) ?
$opt{'database'} : "";
874 my $html = defined($::opt_html
) ?
"-html" : "";
875 my $no_headers = defined($::opt_n
) ?
"-noheader" : "-header";
878 "$database_driver $pass_through_options $sep ".
879 "$no_headers $html $database";
880 $interactive_command = $batch_command;
881 return($batch_command,$interactive_command);
884 sub influx_commands
{
885 my ($database_driver,%opt) = (@_);
886 my $influx = find_command_in_path
("influx") ||
887 die ("influx not in path");
888 if(defined($::opt_s
)) {
889 die "Field separator not implemented for influx";
892 defined($opt{'password'}) ?
"-password=".$opt{'password'} : "";
893 my $host = defined($opt{'host'}) ?
"-host=".$opt{'host'} : "";
894 my $port = defined($opt{'port'}) ?
"-port=".$opt{'port'} : "";
895 my $user = defined($opt{'user'}) ?
"-username=".$opt{'user'} : "";
896 my $database = defined($opt{'database'}) ?
897 "-database $opt{'database'}" : "-database $ENV{'USER'}";
899 my $format = defined($::opt_json
) ?
"-format json" :
900 defined($::opt_pretty
) ?
"-format json -pretty" :
901 defined($::opt_csv
) ?
"-format csv" : "";
902 my $precision = defined($::opt_precision
) ?
903 "-precision $::opt_precision" : "";
905 my $no_headers = defined($::opt_n
) ?
"--skip-column-names" : "";
907 if($database_driver eq "influxssl") { $ssl="--ssl"; }
910 "$influx $pass_through_options $no_headers $format ".
911 "$precision $ssl $host $user $password $port $database";
912 $interactive_command = $batch_command;
913 return($batch_command,$interactive_command);
916 # Return the code for 'show processlist' in the chosen database dialect
918 my $dbdriver = shift;
921 ("mysql" => "show processlist;",
922 "postgresql" => ("SELECT ".
923 " datname AS database,".
924 " usename AS username,".
925 " now()-xact_start AS runtime,".
927 "FROM pg_stat_activity ".
928 "ORDER BY runtime DESC;"),
929 "oracle" => ("SELECT ".
930 ' CPU_TIME/100000, SYS.V_$SQL.SQL_TEXT, USERNAME '.
932 ' SYS.V_$SQL, SYS.V_$SESSION '.
934 ' SYS.V_$SQL.SQL_ID = SYS.V_$SESSION.SQL_ID(+) '.
935 "AND username IS NOT NULL ".
936 "ORDER BY CPU_TIME DESC;"),
937 "influx" => "show queries;",
939 if($statement{$dbdriver}) {
940 return $statement{$dbdriver};
942 print STDERR
"processlist is not implemented for $dbdriver\n";
947 # Return the code for 'show tables' in the chosen database dialect
949 my $dbdriver = shift;
952 ("mysql" => "show tables;",
953 "postgresql" => '\dt',
954 "oracle" => ("SELECT object_name ".
955 "FROM user_objects ".
956 "WHERE object_type = 'TABLE';"),
957 "sqlite" => ".tables",
958 "sqlite3" => ".tables",
959 "influx" => "show measurements;",
961 if($statement{$dbdriver}) {
962 return $statement{$dbdriver};
964 print STDERR
"tablelist is not implemented for $dbdriver\n";
969 # Return the code for 'show databases' in the chosen database dialect
971 my $dbdriver = shift;
974 ("mysql" => "show databases;",
975 "postgresql" => ("SELECT datname FROM pg_database ".
976 "WHERE datname NOT IN ('template0','template1','postgres') ".
977 "ORDER BY datname ASC;"),
978 "oracle" => ("select * from user_tablespaces;"),
979 "influx" => "show databases;",
981 if($statement{$dbdriver}) {
982 return $statement{$dbdriver};
984 print STDERR
"dblist is not implemented for $dbdriver\n";
989 # Return the code for 'show database size' in the chosen database dialect
991 my $dbdriver = shift;
994 if(defined $dburl{'database'}) {
998 ' table_schema "database", '.
999 ' SUM(data_length+index_length) "bytes", '.
1000 ' SUM(data_length+index_length)/1024/1024 "megabytes" '.
1001 'FROM information_schema.TABLES '.
1002 "WHERE table_schema = '$dburl{'database'}'".
1003 'GROUP BY table_schema;'),
1005 "SELECT '$dburl{'database'}' AS database, ".
1006 "pg_database_size('$dburl{'database'}') AS bytes, ".
1007 "pg_size_pretty(pg_database_size('$dburl{'database'}')) AS human_readabable "),
1009 "SELECT ".(undef_as_zero
(-s
$dburl{'database'}))." AS bytes;"),
1011 "SELECT ".(undef_as_zero
(-s
$dburl{'database'}))." AS bytes;"),
1017 ' table_schema "database", '.
1018 ' SUM(data_length+index_length) "bytes", '.
1019 ' SUM(data_length+index_length)/1024/1024 "megabytes" '.
1020 'FROM information_schema.TABLES '.
1021 'GROUP BY table_schema;'),
1023 'SELECT datname AS database, pg_database_size(datname) AS bytes, '.
1024 'pg_size_pretty(pg_database_size(datname)) AS human_readabable '.
1025 'FROM (SELECT datname FROM pg_database) a;'),
1027 "SELECT 0 AS bytes;"),
1029 "SELECT 0 AS bytes;"),
1032 if($statement{$dbdriver}) {
1033 return $statement{$dbdriver};
1035 print STDERR
"dbsize is not implemented for $dbdriver\n";
1041 # Return the code for 'show table size' in the chosen database dialect
1043 my $dbdriver = shift;
1044 my $database = shift;
1047 "SELECT relname, relpages*8 AS kb, reltuples::int AS \"live+dead rows\" ".
1049 "ORDER BY relpages DESC;"),
1051 "select table_name, TABLE_ROWS, DATA_LENGTH,INDEX_LENGTH from INFORMATION_SCHEMA.tables;"),
1053 if($statement{$dbdriver}) {
1054 return $statement{$dbdriver};
1056 print STDERR
"table size is not implemented for $dbdriver\n";
1061 sub is_stdin_terminal
{
1065 sub find_command_in_path
{
1066 # Find the command if it exists in the current path
1067 my $command = shift;
1068 my $path = `which $command`;
1076 print map{ "$_\n" } @_;
1079 print "sql [-hnr] [--table-size] [--db-size] [-p pass-through] [-s string] dburl [command]\n";
1084 $alias =~ s/^(sql:)*//; # Accept aliases prepended with sql:
1085 if ($alias !~ /^:/) {
1092 ($path) = readlink($0) =~ m
|^(.*)/|;
1094 ($path) = $0 =~ m
|^(.*)/|;
1097 my @deprecated = ("$ENV{HOME}/.dburl.aliases",
1098 "$path/dburl.aliases", "$path/dburl.aliases.dist");
1101 print STDERR
"$_ is deprecated. Use .sql/aliases instead (read man sql)\n";
1105 check_permissions
("$ENV{HOME}/.sql/aliases");
1106 check_permissions
("$ENV{HOME}/.dburl.aliases");
1107 my @search = ("$ENV{HOME}/.sql/aliases",
1108 "$ENV{HOME}/.dburl.aliases", "/etc/sql/aliases",
1109 "$path/dburl.aliases", "$path/dburl.aliases.dist");
1110 for my $alias_file (@search) {
1111 if(-r
$alias_file) {
1112 push @urlalias, `cat "$alias_file"`;
1115 my ($alias_part,$rest) = $alias=~/(:\w*)(.*)/;
1116 # If we saw this before: we have an alias loop
1117 if(grep {$_ eq $alias_part } @Private::seen_aliases
) {
1118 print STDERR
"$alias_part is a cyclic alias\n";
1121 push @Private::seen_aliases
, $alias_part;
1126 /^$alias_part\s+(\S+.*)/ and do { $dburl = $1; last; }
1130 return get_alias
($dburl.$rest);
1132 Usage
("$alias is not defined in @search");
1137 sub check_permissions
{
1142 my $username = (getpwuid($<))[0];
1143 print STDERR
"$file should be owned by $username: chown $username $file\n";
1145 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
1146 $atime,$mtime,$ctime,$blksize,$blocks) = stat($file);
1148 my $username = (getpwuid($<))[0];
1149 print STDERR
"$file should be only be readable by $username: chmod 600 $file\n";
1157 # sql:mysql://[[user][:password]@][host][:port]/[database[?sql query]]
1159 if($url=~m
!(?
:sql
:)?
# You can prefix with 'sql:'
1160 ((?
:oracle
|ora
|mysql
|pg
|postgres
|postgresql
|influx
|influxdb
)(?
:s
|ssl
|)|
1161 (?
:sqlite
|sqlite2
|sqlite3
)):// # Databasedriver ($1)
1163 ([^:@
/][^:@
]*|) # Username ($2)
1165 :([^@
]*) # Password ($3)
1168 ([^:/]*)?
# Hostname ($4)
1171 ([^/]*)?
# Port ($5)
1175 ([^?
/]*)?
# Database ($6)
1183 $options{databasedriver
} = undef_if_empty
(uri_unescape
($1));
1184 $options{user
} = undef_if_empty
(uri_unescape
($2));
1185 $options{password
} = undef_if_empty
(uri_unescape
($3));
1186 $options{host
} = undef_if_empty
(uri_unescape
($4));
1187 $options{port
} = undef_if_empty
(uri_unescape
($5));
1188 $options{database
} = undef_if_empty
(uri_unescape
($6))
1190 $options{query
} = undef_if_empty
(uri_unescape
($7));
1191 debug
("dburl $url\n");
1192 debug
("databasedriver ",$options{databasedriver
}, " user ", $options{user
},
1193 " password ", $options{password
}, " host ", $options{host
},
1194 " port ", $options{port
}, " database ", $options{database
},
1195 " query ",$options{query
}, "\n");
1197 Usage
("$url is not a valid DBURL");
1204 # Copied from http://cpansearch.perl.org/src/GAAS/URI-1.55/URI/Escape.pm
1205 # to avoid depending on URI::Escape
1206 # This section is (C) Gisle Aas.
1207 # Note from RFC1630: "Sequences which start with a percent sign
1208 # but are not followed by two hexadecimal characters are reserved
1209 # for future extension"
1211 if (@_ && wantarray) {
1212 # not executed for the common case of a single argument
1213 my @str = ($str, @_); # need to copy
1215 s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
1219 $str =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg if defined $str;
1223 sub undef_if_empty
{
1224 if(defined($_[0]) and $_[0] eq "") {
1238 "GNU $Global::progname $Global::version",
1239 "Copyright (C) 2009,2010,2011,2012,2013,2014,2015,2016,2017",
1240 "Ole Tange and Free Software Foundation, Inc.",
1241 "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>",
1242 "This is free software: you are free to change and redistribute it.",
1243 "GNU $Global::progname comes with no warranty.",
1245 "Web site: http://www.gnu.org/software/${Global::progname}\n",
1246 "When using GNU $Global::progname for a publication please cite:\n",
1247 "O. Tange (2011): GNU SQL - A Command Line Tool for Accessing Different",
1248 "Databases Using DBURLs, ;login: The USENIX Magazine, April 2011:29-32.\n"
1261 print "$Global::progname [options] dburl [sqlcommand]\n";
1262 print "$Global::progname [options] dburl < sql_command_file\n";
1264 print "See 'man $Global::progname' for the options\n";
1269 $Global::debug
or return;
1270 @_ = grep { defined $_ ?
$_ : "" } @_;
1274 $::opt_skip_first_line
= $::opt_shebang
= 0;