Add Lock info, show blocked or 'wait for' sessions.
[yasql.git] / yasql.in
blobca139f17d873fa1cbfe0c9c7fcbdd8fa7ed0ad38
1 #! /usr/bin/env perl
2 # vim: set tabstop=2 smartindent shiftwidth=2 expandtab :
4 # Name: yasql - Yet Another SQL*Plus replacement
6 # See POD documentation at end
8 # $Id: yasql,v 1.83 2005/05/09 16:57:13 qzy Exp qzy $
10 # Copyright (C) 2000 Ephibian, Inc.
11 # Copyright (C) 2005 iMind.dev, Inc.
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; either version 2
16 # of the License, or (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 # Yasql was originally developed by Nathan Shafer at Ephibian, Inc.
28 # Now it is mainly developed and maintained by Balint Kozman at iMind.dev, Inc.
30 # email: nshafer@ephibian.com
31 # email: qzy@users.sourceforge.net
32 # email: jpnangle@users.sourceforge.net
35 use strict;
37 use SelfLoader;
39 use DBI;
40 use Term::ReadLine;
41 use Data::Dumper;
42 use Benchmark;
43 use Getopt::Long;
45 # Load DBD::Oracle early to work around SunOS bug. See
46 # http://article.gmane.org/gmane.comp.lang.perl.modules.dbi.general/207
48 require DBD::Oracle;
50 #Globals
51 use vars qw(
52 $VERSION $Id $dbh $cursth @dbparams $dbuser $dbversion $term $term_type
53 $features $attribs $last_history $num_connects $connected $running_query
54 @completion_list @completion_possibles $completion_built $opt_host $opt_sid
55 $opt_port $opt_debug $opt_bench $opt_nocomp $opt_version $qbuffer
56 $last_qbuffer $fbuffer $last_fbuffer $quote $inquotes $inplsqlblock $increate
57 $incomment $csv_filehandle_open $csv_max_lines $nohires $notextcsv $csv
58 $sysconf $sysconfdir $quitting $sigintcaught %conf %prompt $prompt_length
59 @sqlpath %set $opt_batch $opt_notbatch $opt_headers
62 select((select(STDOUT), $| = 1)[0]); #unbuffer STDOUT
64 $sysconfdir = "/etc";
65 $sysconf = "$sysconfdir/yasql.conf";
67 # try to include Time::HiRes for fine grained benchmarking
68 eval q{
69 use Time::HiRes qw (gettimeofday tv_interval);
72 # try to include Text::CSV_XS for input and output of CSV data
73 eval q{
74 use Text::CSV_XS;
76 if($@) {
77 $notextcsv = 1;
80 # install signal handlers
81 sub setup_sigs {
82 $SIG{INT} = \&sighandle;
83 $SIG{TSTP} = 'DEFAULT';
84 $SIG{TERM} = \&sighandle;
86 setup_sigs();
88 # install a filter on the __WARN__ handler so that we can get rid of
89 # DBD::Oracle's stupid ORACLE_HOME warning. It would warn even if we don't
90 # connect using a TNS name, which doesn't require access to the ORACLE_HOME
91 $SIG{__WARN__} = sub{
92 warn(@_) unless $_[0] =~ /environment variable not set!/;
95 # initialize the whole thing
96 init();
98 if($@) {
99 if(!$opt_batch) {
100 wrn("Time::HiRes not installed. Please install if you want benchmark times "
101 ."to include milliseconds.");
103 $nohires = 1;
107 $connected = 1;
109 # start the interface
110 interface();
112 # end
114 ################################################################################
115 ########### non-self-loaded functions ########################################
117 sub BEGIN {
118 $VERSION = 'unknown';
121 sub argv_sort {
122 if($a =~ /^\@/ && $b !~ /^\@/) {
123 return 1;
124 } elsif($a !~ /^\@/ && $b =~ /^\@/) {
125 return -1;
126 } else {
127 return 0;
131 sub sighandle {
132 my($sig) = @_;
133 debugmsg(3, "sighandle called", @_);
135 $SIG{$sig} = \&sighandle;
137 if($sig =~ /INT|TERM|TSTP/) {
138 if($quitting) {
139 # then we've already started quitting and so we just try to force exit
140 # without the graceful quit
141 print STDERR "Attempting to force exit...\n";
142 exit();
145 if($sigintcaught) {
146 # the user has alrady hit INT and so we now force an exit
147 print STDERR "Caught another SIG$sig\n";
148 quit(undef, 1);
149 } else {
150 $sigintcaught = 1;
153 if($running_query) {
154 if(defined $cursth) {
155 print STDERR "Attempting to cancel query...\n";
156 debugmsg(1, "canceling statement handle");
157 my $ret = $cursth->cancel();
158 $cursth->finish;
160 } elsif(!$connected) {
161 quit();
163 if(defined $cursth) {
164 print STDERR "Attempting to cancel query...\n";
165 debugmsg(1, "canceling statement handle");
166 my $ret = $cursth->cancel();
167 $cursth->finish;
171 } elsif($sig eq 'ALRM') {
173 if(defined $dbh) {
174 wrn("Connection lost (timeout: $conf{connection_timeout})");
175 quit(1);
176 } else {
177 lerr("Could not connect to database, timed out. (timeout: "
178 ."$conf{connection_timeout})");
183 sub END {
184 debugmsg(3, "END called", @_);
186 # save the history buffer
187 if($term_type && $term_type eq 'gnu' && $term->history_total_bytes()) {
188 debugmsg(1, "Writing history");
189 unless($term->WriteHistory($conf{history_file})) {
190 wrn("Could not write history file to $conf{history_file}. "
191 ."History not saved");
196 ################################################################################
197 ########### self-loaded functions ##############################################
199 #__DATA__
201 sub init {
202 # call GetOptions to parse the command line
203 my $opt_help;
204 Getopt::Long::Configure( qw(permute) );
205 $Getopt::Long::ignorecase = 0;
206 usage(1) unless GetOptions(
207 "debug|d:i" => \$opt_debug,
208 "host|H=s" => \$opt_host,
209 "port|p=s" => \$opt_port,
210 "sid|s=s" => \$opt_sid,
211 "help|h|?" => \$opt_help,
212 "nocomp|A" => \$opt_nocomp,
213 "bench|benchmark|b" => \$opt_bench,
214 "version|V" => \$opt_version,
215 "batch|B" => \$opt_batch,
216 "interactive|I" => \$opt_notbatch,
219 # set opt_debug to 1 if it's defined, which means the user just put -d or
220 # --debug without an integer argument
221 $opt_debug = 1 if !$opt_debug && defined $opt_debug;
223 $opt_batch = 0 if $opt_notbatch;
225 $opt_batch = 1 unless defined $opt_batch || -t STDIN;
227 debugmsg(3, "init called", @_);
228 # This reads the command line then initializes the DBI and Term::ReadLine
229 # packages
231 $sigintcaught = 0;
232 $completion_built = 0;
234 usage(0) if $opt_help;
236 # Output startup string
237 if(!$opt_batch) {
238 print STDERR "\n";
239 print STDERR "YASQL version $VERSION Copyright (c) 2000-2001 Ephibian, Inc, 2005 iMind.dev.\n";
240 print STDERR '$Id: yasql,v 1.83 2005/05/09 02:07:13 qzy Exp qzy $' . "\n";
243 if($opt_version) {
244 print STDERR "\n";
245 exit(0);
248 if(!$opt_batch) {
249 print STDERR "Please type 'help' for usage instructions\n";
250 print STDERR "\n";
253 # parse the config files. We first look for ~/.yasqlrc, then
254 # /etc/yasql.conf
255 # first set up the defaults
256 %conf = (
257 connection_timeout => 20,
258 max_connection_attempts => 3,
259 history_file => '~/.yasql_history',
260 pager => '/bin/more',
261 auto_commit => 0,
262 commit_on_exit => 1,
263 long_trunc_ok => 1,
264 long_read_len => 80,
265 edit_history => 1,
266 auto_complete => 1,
267 extended_benchmarks => 0,
268 prompt => '%U%H',
269 column_wildcards => 0,
270 extended_complete_list => 0,
271 command_complete_list => 1,
272 sql_query_in_error => 0,
273 nls_date_format => 'YYYY-MM-DD HH24:MI:SS',
274 complete_tables => 1,
275 complete_columns => 1,
276 complete_objects => 1,
277 fast_describe => 1,
278 server_output => 2000,
281 my $config_file;
282 if( -e $ENV{YASQLCONF} ) {
283 $config_file = $ENV{YASQLCONF};
284 } elsif(-e "$ENV{HOME}/.yasqlrc") {
285 $config_file = "$ENV{HOME}/.yasqlrc";
286 } elsif(-e $sysconf) {
287 $config_file = $sysconf;
290 if($config_file) {
291 debugmsg(2, "Reading config: $config_file");
292 open(CONFIG, "$config_file");
293 while(<CONFIG>) {
294 chomp;
295 s/#.*//;
296 s/^\s+//;
297 s/\s+$//;
298 next unless length;
299 my($var, $value) = split(/\s*=\s*/, $_, 2);
300 $var = 'auto_commit' if $var eq 'AutoCommit';
301 $var = 'commit_on_exit' if $var eq 'CommitOnExit';
302 $var = 'long_trunc_ok' if $var eq 'LongTruncOk';
303 $var = 'long_read_len' if $var eq 'LongReadLen';
304 $conf{$var} = $value;
305 debugmsg(3, "Setting option [$var] to [$value]");
309 if (($conf{server_output} > 0) && ($conf{server_output} < 2000)) {
310 $conf{server_output} = 2000;
312 if ($conf{server_output} > 1000000) {
313 $conf{server_output} = 1000000;
316 ($conf{history_file}) = glob($conf{history_file});
318 debugmsg(3,"Conf: [" . Dumper(\%conf) . "]");
320 # Create a Text::CSV object
321 unless($notextcsv) {
322 $csv = new Text::CSV_XS( { binary => 1 } );
325 # Change the process name to just 'yasql' to somewhat help with security.
326 # This is not bullet proof, nor is it supported on all platforms. Those that
327 # don't support this will just fail silently.
328 debugmsg(2, "Process name: $0");
329 $0 = 'yasql';
331 # Parse the SQLPATH environment variable if it exists
332 if($ENV{SQLPATH}) {
333 @sqlpath = split(/;/, $ENV{SQLPATH});
336 # If the user set the SID on the command line, we'll overwrite the
337 # environment variable so that DBI sees it.
338 #print "Using SID $opt_sid\n" if $opt_sid;
339 $ENV{ORACLE_SID} = $opt_sid if $opt_sid;
341 # output info about the options given
342 print STDERR "Debugging is on\n" if $opt_debug;
343 DBI->trace(1) if $opt_debug > 3;
345 # Extending on from Oracle's conventions, try and obtain an early indication
346 # of ora_session_mode from AS SYSOPER, AS SYSDBA options. Be flexible :-)
347 my $ora_session_mode = 0;
348 my $osmp = '';
349 if (lc($ARGV[-2]) eq 'as') {
350 $ora_session_mode = 2 if lc($ARGV[-1]) eq 'sysdba';
351 $ora_session_mode = 4 if lc($ARGV[-1]) eq 'sysoper';
352 pop @ARGV;
353 pop @ARGV;
354 } elsif (lc($ARGV[1]) eq 'as') {
355 $ora_session_mode = 2 if lc($ARGV[2]) eq 'sysdba';
356 $ora_session_mode = 4 if lc($ARGV[2]) eq 'sysoper';
357 @ARGV = ($ARGV[0], @ARGV[3..$#ARGV]);
360 # set up DBI
361 if(@ARGV == 0) {
362 # nothing was provided
363 debugmsg(2, "No command line args were found");
364 $dbh = db_connect(1, $ora_session_mode);
365 } else {
366 debugmsg(2, "command line args found!");
367 debugmsg(2, @ARGV);
368 # an argument was given!
370 my $script = 0;
371 if(substr($ARGV[0], 0, 1) eq '@') {
372 # no logon string was given, must be a script
373 debugmsg(2, "Found: no logon, script name");
374 my($script_name, @script_params) = @ARGV;
375 $script = 1;
377 $dbh = db_connect(1, $ora_session_mode);
379 run_script($script_name);
380 } elsif(substr($ARGV[0], 0, 1) ne '@' && substr($ARGV[1], 0, 1) eq '@') {
381 # A logon string was given as well as a script file
382 debugmsg(2, "Found: login string, script name");
383 my($logon_string, $script_name, @script_params) = @ARGV;
384 $script = 1;
386 my($ora_session_mode2, $username, $password, $connect_string)
387 = parse_logon_string($logon_string);
388 $ora_session_mode = $ora_session_mode2 if $ora_session_mode2;
389 $dbh = db_connect(1, $ora_session_mode, $username, $password, $connect_string);
391 run_script($script_name);
392 } elsif(@ARGV == 1 && substr($ARGV[0], 0, 1) ne '@') {
393 # only a logon string was given
394 debugmsg(2, "Found: login string, no script name");
395 my($logon_string) = @ARGV;
397 my($ora_session_mode2, $username, $password, $connect_string)
398 = parse_logon_string($logon_string);
399 $ora_session_mode = $ora_session_mode2 if $ora_session_mode2;
400 $dbh = db_connect(1, $ora_session_mode, $username, $password, $connect_string);
401 } else {
402 usage(1);
405 if ($conf{server_output} > 0) {
406 $dbh->func( $conf{server_output}, 'dbms_output_enable' );
407 $set{serveroutput} = 1;
410 # Quit if one or more scripts were given on the command-line
411 quit(0) if $script;
414 if (!$opt_batch) {
415 setup_term() unless $term;
418 # set up the pager
419 $conf{pager} = $ENV{PAGER} if $ENV{PAGER};
422 sub setup_term {
423 # set up the Term::ReadLine
424 $term = new Term::ReadLine('YASQL');
425 $term->ornaments(0);
426 $term->MinLine(0);
428 debugmsg(1, "Using " . $term->ReadLine());
430 if($term->ReadLine eq 'Term::ReadLine::Gnu') {
431 # Term::ReadLine::Gnu specific setup
432 $term_type = 'gnu';
434 $attribs = $term->Attribs();
435 $features = $term->Features();
437 $term->stifle_history(500);
438 if($opt_debug >= 4) {
439 foreach(sort keys(%$attribs)) {
440 debugmsg(4,"[term-attrib] $_: $attribs->{$_}");
442 foreach(sort keys(%$features)) {
443 debugmsg(4,"[term-feature] $_: $features->{$_}");
447 # read in the ~/.yasql_history file
448 if(-e $conf{history_file}) {
449 unless($term->ReadHistory($conf{history_file})) {
450 wrn("Could not read $conf{history_file}. History not restored");
452 } else {
453 print STDERR "Creating $conf{history_file} to store your command line history\n";
454 open(HISTORY, ">$conf{history_file}")
455 or wrn("Could not create $conf{history_file}: $!");
456 close(HISTORY);
459 $last_history = $term->history_get($term->{history_length});
461 $attribs->{completion_entry_function} = \&complete_entry_function;
462 my $completer_word_break_characters
463 = $attribs->{completer_word_break_characters};
464 $completer_word_break_characters =~ s/[a-zA-Z0-9_\$\#]//g;
465 $attribs->{completer_word_break_characters}
466 = $completer_word_break_characters;
467 #$attribs->{catch_signals} = 0;
468 } elsif($term->ReadLine eq 'Term::ReadLine::Perl') {
469 # Term::ReadLine::Perl specific setup
470 $term_type = 'perl';
471 if($opt_debug >= 4) {
472 foreach(sort keys(%{$term->Features()})) {
473 debugmsg(4,"[term-feature] $_: $attribs->{$_}");
479 if ($term->ReadLine eq 'Term::ReadLine::Stub') {
480 wrn("Neither Term::ReadLine::Gnu or Term::ReadLine::Perl are installed.\n"
481 . "Please install from CPAN for advanced functionality. Until then "
482 . "YASQL will run\ncrippled. (like possibly not having command history "
483 . "or line editing...\n");
487 sub parse_logon_string {
488 debugmsg(3, "parse_logon_string called", @_);
490 my($arg) = @_;
491 my($ora_session_mode, $username, $password, $connect_string);
493 # strip off AS SYSDBA / AS SYSOPER first
494 if($arg =~ /^(.*)\s+as\s+sys(\w+)\s*$/i) {
495 $ora_session_mode = 2 if lc($2) eq 'dba';
496 $ora_session_mode = 4 if lc($2) eq 'oper';
497 $arg = $1 if $ora_session_mode;
498 $ora_session_mode = 0 unless $ora_session_mode;
500 if($arg =~ /^\/$/) {
501 $username = '';
502 $password = '';
503 $connect_string = 'external';
504 return($ora_session_mode, $username, $password, $connect_string);
505 } elsif($arg eq 'internal') {
506 $username = '';
507 $password = '';
508 $connect_string = 'external';
509 $ora_session_mode = 2;
510 return($ora_session_mode, $username, $password, $connect_string);
511 } elsif($arg =~ /^([^\/]+)\/([^\@]+)\@(.*)$/) {
512 #username/password@connect_string
513 $username = $1;
514 $password = $2;
515 $connect_string = $3;
516 return($ora_session_mode, $username, $password, $connect_string);
517 } elsif($arg =~ /^([^\@]+)\@(.*)$/) {
518 # username@connect_string
519 $username = $1;
520 $password = '';
521 $connect_string = $2;
522 return($ora_session_mode, $username, $password, $connect_string);
523 } elsif($arg =~ /^([^\/]+)\/([^\@]+)$/) {
524 # username/password
525 $username = $1;
526 $password = $2;
527 $connect_string = '';
528 return($ora_session_mode, $username, $password, $connect_string);
529 } elsif($arg =~ /^([^\/\@]+)$/) {
530 # username
531 $username = $1;
532 $password = $2;
533 $connect_string = '';
534 return($ora_session_mode, $username, $password, $connect_string);
535 } elsif($arg =~ /^\@(.*)$/) {
536 # @connect_string
537 $username = '';
538 $password = '';
539 $connect_string = $1;
540 return($ora_session_mode, $username, $password, $connect_string);
541 } else {
542 return(undef,undef,undef,undef);
546 sub populate_completion_list {
547 my($inline_print, $current_table_name) = @_;
548 debugmsg(3, "populate_completion_list called", @_);
550 # grab all the table and column names and put them in @completion_list
552 if($inline_print) {
553 $| = 1;
554 print STDERR "...";
555 } else {
556 print STDERR "Generating auto-complete list...\n";
559 if($conf{extended_complete_list}) {
560 my @queries;
561 if($conf{complete_tables}) {
562 push(@queries, 'select table_name from all_tables');
564 if($conf{complete_columns}) {
565 push(@queries, 'select column_name from all_tab_columns');
567 if($conf{complete_objects}) {
568 push(@queries, 'select object_name from all_objects');
571 my $sqlstr = join(' union ', @queries);
572 debugmsg(3, "query: [$sqlstr]");
574 my $sth = $dbh->prepare($sqlstr)
575 or query_err('prepare', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
576 $sth->execute()
577 or query_err('execute', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
578 while(my $res = $sth->fetchrow_array()) {
579 push(@completion_list, $res);
581 } else {
582 my @queries;
583 if($conf{complete_tables}) {
584 push(@queries, "select 'table-' || table_name from user_tables");
586 if($conf{complete_columns}) {
587 push(@queries, "select 'column-' || column_name from user_tab_columns");
589 if($conf{complete_objects}) {
590 push(@queries, "select 'object-' || object_name from user_objects");
593 my $sqlstr = join(' union ', @queries);
594 debugmsg(3, "query: [$sqlstr]");
596 my $sth = $dbh->prepare($sqlstr)
597 or query_err('prepare', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
598 $sth->execute()
599 or query_err('execute', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
600 while(my $res = $sth->fetchrow_array()) {
601 push(@completion_list, $res);
605 if ($conf{command_complete_list}) {
606 push(@completion_list, "command-create", "command-select", "command-insert", "command-update", "command-delete from", "command-from", "command-execute", "command-show", "command-describe", "command-drop");
607 push(@completion_list, "show-objects", "show-tables", "show-indexes", "show-sequences", "show-views", "show-functions", "show-constraints", "show-keys", "show-checks", "show-triggers", "show-query", "show-dimensions", "show-clusters", "show-procedures", "show-packages", "show-indextypes", "show-libraries", "show-materialized views", "show-snapshots", "show-synonyms", "show-waits", "show-processes", "show-errors", "show-user", "show-users", "show-uid", "show-plan", "show-database links", "show-dblinks");
610 if ($current_table_name) {
612 my @queries;
613 push(@queries, "select 'current_column-$current_table_name.' || column_name from user_tab_columns where table_name=\'".uc($current_table_name)."\'");
615 my $sqlstr = join(' union ', @queries);
616 debugmsg(3, "query: [$sqlstr]");
618 my $sth = $dbh->prepare($sqlstr)
619 or query_err('prepare', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
620 $sth->execute()
621 or query_err('execute', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
622 while(my $res = $sth->fetchrow_array()) {
623 push(@completion_list, $res);
627 setup_sigs();
629 if($inline_print) {
630 print "\r";
631 print "\e[K";
632 $| = 0;
633 $term->forced_update_display();
637 sub complete_entry_function {
638 my($word, $state) = @_;
639 debugmsg(3, "complete_entry_function called", @_);
640 # This is called by Term::ReadLine::Gnu when a list of matches needs to
641 # be generated. It takes a string that is the word to be completed and
642 # a state number, which should increment every time it's called.
644 return unless $connected;
646 my $line_buffer = $attribs->{line_buffer};
647 debugmsg(4, "line_buffer: [$line_buffer]");
649 if($line_buffer =~ /^\s*\@/) {
650 return($term->filename_completion_function(@_));
653 unless($completion_built) {
654 unless($opt_nocomp || !$conf{auto_complete}) {
655 populate_completion_list(1);
657 $completion_built = 1;
660 if($state == 0) {
661 # compute all the possibilies and put them in @completion_possibles
662 @completion_possibles = ();
663 my $last_char = substr($word,length($word)-1,1);
665 debugmsg(2,"last_char: [$last_char]");
667 my @grep = ();
668 if ($line_buffer =~ /select(?!.*(?:from|where))[\s\w\$\#_,]*\.[\w_]*$/) {
669 # This case is for "select mytable.mycolumn" type lines
670 my $current_table_name = $line_buffer;
671 $current_table_name =~ s/(select.*)(\s)([\w_]+)(\.)([\w_]*)$/$3/;
672 debugmsg(3, "current table name: $current_table_name");
674 unless($opt_nocomp || !$conf{auto_complete}) {
675 populate_completion_list(1, $current_table_name);
678 debugmsg(4, "select table.column");
680 push(@grep, '^current_column-');
681 } elsif($line_buffer =~ /select(?!.*(?:from|where))[\s\w\$\#_,]+$/) {
682 debugmsg(4, "select ...");
683 push(@grep, '^column-', '^table-');
684 } elsif($line_buffer =~ /from(?!.*where)[\s\w\$\#_,]*$/) {
685 debugmsg(4, "from ...");
686 push(@grep, '^table-');
687 } elsif($line_buffer =~ /where[\s\w\$\#_,]*$/) {
688 debugmsg(4, "where ...");
689 push(@grep, '^column-');
690 } elsif($line_buffer =~ /update(?!.*set)[\s\w\$\#_,]*$/) {
691 debugmsg(4, "where ...");
692 push(@grep, '^table-');
693 } elsif($line_buffer =~ /set[\s\w\$\#_,]*$/) {
694 debugmsg(4, "where ...");
695 push(@grep, '^column-');
696 } elsif($line_buffer =~ /insert.*into(?!.*values)[\s\w\$\#_,]*$/) {
697 debugmsg(4, "where ...");
698 push(@grep, '^table-');
699 } elsif($line_buffer =~ /^\s*show\s\w*/) {
700 push(@grep, 'show-');
701 } else {
702 push(@grep, '');
704 debugmsg(2,"grep: [@grep]");
706 my $use_lower;
707 if($last_char =~ /^[A-Z]$/) {
708 $use_lower = 0;
709 } else {
710 $use_lower = 1;
712 foreach my $grep (@grep) {
713 foreach my $list_item (grep(/$grep/, @completion_list)) {
714 my $item = $list_item;
715 $item =~ s/^\w*-//;
716 eval { #Trap errors
717 if($item =~ /^\Q$word\E/i) {
718 push(@completion_possibles,
719 ($use_lower ? lc($item) : uc($item))
723 debugmsg(2, "Trapped error in complete_entry_function eval: $@") if $@;
726 debugmsg(3,"possibles: [@completion_possibles]");
729 # return the '$state'th element of the possibles
730 return($completion_possibles[$state] || undef);
733 sub db_reconnect {
734 debugmsg(3, "db_reconnect called", @_);
735 # This first disconnects the database, then tries to reconnect
737 print "Reconnecting...\n";
739 commit_on_exit();
741 if (defined $dbh) {
742 if (not $dbh->disconnect()) {
743 warn "Disconnect failed: $DBI::errstr\n";
744 return;
748 $dbh = db_connect(1, @dbparams);
751 sub db_connect {
752 my($die_on_error, $ora_session_mode, $username, $password, $connect_string) = @_;
753 debugmsg(3, "db_connect called", @_);
754 # Tries to connect to the database, prompting for username and password
755 # if not given. There are several cases that can happen:
756 # connect_string is present:
757 # ORACLE_HOME has to exist and the driver tries to make a connection to
758 # given connect_string.
759 # connect_string is not present:
760 # $opt_host is set:
761 # Connect to $opt_host on $opt_sid. Specify port only if $opt_port is
762 # set
763 # $opt_host is not set:
764 # Try to make connection to the default database by not specifying any
765 # host or connect string
767 my($dbhandle, $dberr, $dberrstr, $this_prompt_host, $this_prompt_user);
769 debugmsg(1,"ora_session_mode: [$ora_session_mode] username: [$username] password: [$password] connect_string: [$connect_string]");
771 # The first thing we're going to check is that the Oracle DBD is available
772 # since it's a sorta required element =)
773 my @drivers = DBI->available_drivers();
774 my $found = 0;
775 foreach(@drivers) {
776 if($_ eq "Oracle") {
777 $found = 1;
780 unless($found) {
781 lerr("Could not find DBD::Oracle... please install. Available drivers: "
782 .join(", ", @drivers) . ".\n");
784 #print "drivers: [" . join("|", @drivers) . "]\n";
786 # Now we can attempt a connection to the database
787 my $attributes = {
788 RaiseError => 0,
789 PrintError => 0,
790 AutoCommit => $conf{auto_commit},
791 LongReadLen => $conf{long_read_len},
792 LongTruncOk => $conf{long_trunc_ok},
793 ora_session_mode => $ora_session_mode
796 if($connect_string eq 'external') {
797 # the user wants to connect with external authentication
799 check_oracle_home();
801 # install alarm signal handle
802 $SIG{ALRM} = \&sighandle;
803 alarm($conf{connection_timeout});
805 if(!$opt_batch) {
806 print "Attempting connection to local database\n";
808 $dbhandle = DBI->connect('dbi:Oracle:',undef,undef,$attributes)
809 or do {
810 $dberr = $DBI::err;
811 $dberrstr = $DBI::errstr;
814 $this_prompt_host = $ENV{ORACLE_SID};
815 $this_prompt_user = $ENV{LOGNAME};
816 alarm(0); # cancel alarm
817 } elsif($connect_string) {
818 # We were provided with a connect string, so we can use the TNS method
820 check_oracle_home();
821 ($ora_session_mode, $username, $password) = get_up($ora_session_mode, $username, $password);
822 $attributes->{ora_session_mode} = $ora_session_mode if $ora_session_mode;
824 my $userstring;
825 if($username) {
826 $userstring = $username . '@' . $connect_string;
827 } else {
828 $userstring = $connect_string;
831 # install alarm signal handle
832 $SIG{ALRM} = \&sighandle;
833 alarm($conf{connection_timeout});
835 if(!$opt_batch) {
836 print "Attempting connection to $userstring\n";
838 $dbhandle = DBI->connect('dbi:Oracle:',$userstring,$password,$attributes)
839 or do {
840 $dberr = $DBI::err;
841 $dberrstr = $DBI::errstr;
844 $this_prompt_host = $connect_string;
845 $this_prompt_user = $username;
846 alarm(0); # cancel alarm
847 } elsif($opt_host) {
848 # attempt a connection to $opt_host
849 my $dsn;
850 $dsn = "host=$opt_host";
851 $dsn .= ";sid=$opt_sid" if $opt_sid;
852 $dsn .= ";port=$opt_port" if $opt_port;
854 ($ora_session_mode, $username, $password) = get_up($ora_session_mode, $username, $password);
855 $attributes->{ora_session_mode} = $ora_session_mode if $ora_session_mode;
857 # install alarm signal handle
858 $SIG{ALRM} = \&sighandle;
859 alarm($conf{connection_timeout});
861 print "Attempting connection to $opt_host\n";
862 debugmsg(1,"dsn: [$dsn]");
863 $dbhandle = DBI->connect("dbi:Oracle:$dsn",$username,$password,
864 $attributes)
865 or do {
866 $dberr = $DBI::err;
867 $dberrstr = $DBI::errstr;
870 $this_prompt_host = $opt_host;
871 $this_prompt_host = "$opt_sid!" . $this_prompt_host if $opt_sid;
872 $this_prompt_user = $username;
873 alarm(0); # cancel alarm
874 } else {
875 # attempt a connection without specifying a hostname or anything
877 check_oracle_home();
878 ($ora_session_mode, $username, $password) = get_up($ora_session_mode, $username, $password);
879 $attributes->{ora_session_mode} = $ora_session_mode if $ora_session_mode;
881 # install alarm signal handle
882 $SIG{ALRM} = \&sighandle;
883 alarm($conf{connection_timeout});
885 print "Attempting connection to local database\n";
886 $dbhandle = DBI->connect('dbi:Oracle:',$username,$password,$attributes)
887 or do {
888 $dberr = $DBI::err;
889 $dberrstr = $DBI::errstr;
892 $this_prompt_host = $ENV{ORACLE_SID};
893 $this_prompt_user = $username;
894 alarm(0); # cancel alarm
897 if($dbhandle) {
898 # Save the parameters for reconnecting
899 @dbparams = ($ora_session_mode, $username, $password, $connect_string);
901 # set the $dbuser global for use elsewhere
902 $dbuser = $username;
903 $num_connects = 0;
904 $prompt{host} = $this_prompt_host;
905 $prompt{user} = $this_prompt_user;
907 # Get the version banner
908 debugmsg(2,"Fetching version banner");
909 my $banner = $dbhandle->selectrow_array(
910 "select banner from v\$version where banner like 'Oracle%'");
911 if(!$opt_batch) {
912 if($banner) {
913 print "Connected to: $banner\n\n";
914 } else {
915 print "Connection successful!\n";
919 if($banner =~ / (\d+)\.(\d+)\.([\d\.]+)/) {
920 my ($major, $minor, $other) = ($1, $2, $3);
921 $dbversion = $major || 8;
924 # Issue a warning about autocommit. It's nice to know...
925 print STDERR "auto_commit is " . ($conf{auto_commit} ? "ON" : "OFF")
926 . ", commit_on_exit is " . ($conf{commit_on_exit} ? "ON" : "OFF")
927 . "\n" unless $opt_batch;
928 } elsif( ($dberr eq '1017' || $dberr eq '1005')
929 && ++$num_connects < $conf{max_connection_attempts}) {
930 $dberrstr =~ s/ \(DBD ERROR: OCISessionBegin\).*//;
931 print "Error: $dberrstr\n\n";
932 #@dbparams = (0,undef,undef,$connect_string);
933 $connect_string = '' if $connect_string eq 'external';
934 $dbhandle = db_connect($die_on_error,$ora_session_mode,undef,undef,$connect_string);
935 } elsif($die_on_error) {
936 lerr("Could not connect to database: $dberrstr [$dberr]");
937 } else {
938 wrn("Could not connect to database: $dberrstr [$dberr]");
939 return(0);
942 # set the NLS_DATE_FORMAT
943 if($conf{nls_date_format}) {
944 debugmsg(2, "setting NLS_DATE_FORMAT to $conf{nls_date_format}");
945 my $sqlstr = "alter session set nls_date_format = '"
946 . $conf{nls_date_format} . "'";
947 $dbhandle->do($sqlstr) or query_err('do', $DBI::errstr, $sqlstr);
950 $connected = 1;
951 return($dbhandle);
954 sub get_prompt {
955 my($prompt_string) = @_;
956 debugmsg(3, "get_prompt called", @_);
957 # This returns a prompt. It can be passed a string which will
958 # be manually put into the prompt. It will be padded on the left with
959 # white space
961 $prompt_length ||= 5; #just in case normal prompt hasn't been outputted
962 debugmsg(2, "prompt_length: [$prompt_length]");
964 if($prompt_string) {
965 my $temp_prompt = sprintf('%' . $prompt_length . 's', $prompt_string . '> ');
966 return($temp_prompt);
967 } else {
968 my $temp_prompt = $conf{prompt} . '> ';
969 my $temp_prompt_host = '@' . $prompt{host} if $prompt{host};
970 $temp_prompt =~ s/\%H/$temp_prompt_host/g;
971 $temp_prompt =~ s/\%U/$prompt{user}/g;
973 $prompt_length = length($temp_prompt);
974 return($temp_prompt);
978 sub get_up {
979 my($ora_session_mode, $username, $password) = @_;
980 debugmsg(3, "get_up called", @_);
982 if(!$opt_batch) {
984 setup_term() unless $term;
986 # Get username/password
987 unless($username) {
988 # prompt for the username
989 $username = $term->readline('Username: ');
990 if($username =~ /^(.*)\s+as\s+sys(\w+)\s*$/i) {
991 $ora_session_mode = 2 if lc($2) eq 'dba';
992 $ora_session_mode = 4 if lc($2) eq 'oper';
993 $username = $1;
996 # Take that entry off of the history list
997 if ($term_type eq 'gnu') {
998 $term->remove_history($term->where_history());
1002 unless($password) {
1003 # prompt for the password, and disable echo
1004 my $orig_redisplay = $attribs->{redisplay_function};
1005 $attribs->{redisplay_function} = \&shadow_redisplay;
1007 $password = $term->readline('Password: ');
1009 $attribs->{redisplay_function} = $orig_redisplay;
1011 # Take that entry off of the history list
1012 if ($term->ReadLine eq "Term::ReadLine::Gnu") {
1013 $term->remove_history($term->where_history());
1018 return($ora_session_mode, $username, $password);
1022 sub check_oracle_home {
1023 # This checks for the ORACLE_HOME environment variable and dies if it's
1024 # not set
1025 lerr("Please set your ORACLE_HOME environment variable!")
1026 unless $ENV{ORACLE_HOME};
1027 return(1);
1030 sub shadow_redisplay {
1031 # The one provided in Term::ReadLine::Gnu was broken
1032 # debugmsg(2, "shadow_redisplay called", @_);
1033 my $OUT = $attribs->{outstream};
1034 my $oldfh = select($OUT); $| = 1; select($oldfh);
1035 print $OUT ("\r", $attribs->{prompt});
1036 $oldfh = select($OUT); $| = 0; select($oldfh);
1039 sub print_non_print {
1040 my($string) = @_;
1042 my @string = unpack("C*", $string);
1043 my $ret_string;
1044 foreach(@string) {
1045 if($_ >= 40 && $_ <= 176) {
1046 $ret_string .= chr($_);
1047 } else {
1048 $ret_string .= "<$_>";
1051 return($ret_string);
1054 sub interface {
1055 debugmsg(3, "interface called", @_);
1056 # this is the main program loop that handles all the user input.
1057 my $input;
1058 my $prompt = get_prompt();
1060 setup_sigs();
1062 # Check if we were interactively called, or do we need to process STDIN
1063 if(-t STDIN) {
1064 while(defined($input = $term->readline($prompt))) {
1065 $sigintcaught = 0;
1066 $prompt = process_input($input, $prompt) || get_prompt();
1067 setup_sigs();
1069 } else {
1070 debugmsg(3, "non-interactive", @_);
1071 debugmsg(3, "\$opt_batch=$opt_batch", @_);
1072 debugmsg(3, "\$opt_batch=$opt_batch", @_);
1073 # Send STDIN to process_input();
1074 while(<STDIN>) {
1075 process_input($_);
1079 quit(0, undef, "\n");
1082 sub process_input {
1083 my($input, $prompt, $add_to_history) = @_;
1084 if (!(defined($add_to_history))) {
1085 $add_to_history = 1;
1087 debugmsg(3, "process_input called", @_);
1089 my $nprompt;
1090 SWITCH: {
1091 if(!$qbuffer) {
1092 # Commands that are only allowed if there is no current buffer
1093 $input =~ /^\s*(?:!|host)\s*(.*)\s*$/i and system($1), last SWITCH;
1094 $input =~ /^\s*\\a\s*$/i and populate_completion_list(), last SWITCH;
1095 $input =~ /^\s*\\\?\s*$/i and help(), last SWITCH;
1096 $input =~ /^\s*help\s*$/i and help(), last SWITCH;
1097 $input =~ /^\s*reconnect\s*$/i and db_reconnect(), last SWITCH;
1098 $input =~ /^\s*\\r\s*$/i and db_reconnect(), last SWITCH;
1099 $input =~ /^\s*conn(?:ect)?\s+(.*)$/i and connect_cmd($1), last SWITCH;
1100 $input =~ /^\s*disc(?:onnect)\s*$/i and disconnect_cmd($1), last SWITCH;
1101 $input =~ /^\s*\@\S+\s*$/i and $nprompt = run_script($input), last SWITCH;
1102 $input =~ /^\s*debug\s*(.*)$/i and debug_toggle($1), last SWITCH;
1103 $input =~ /^\s*autocommit\s*(.*)$/i and autocommit_toggle(), last SWITCH;
1104 $input =~ /^\s*commit/i and commit_cmd(), last SWITCH;
1105 $input =~ /^\s*rollback/i and rollback_cmd(), last SWITCH;
1106 $input =~ /^\s*(show\s*[^;\/\\]+)\s*$/i and show($1, 'table'),last SWITCH;
1107 $input =~ /^\s*(desc\s*[^;\/\\]+)\s*$/i and describe($1, 'table'),
1108 last SWITCH;
1109 $input =~ /^\s*(set\s*[^;\/\\]+)\s*$/i and set_cmd($1), last SWITCH;
1110 $input =~ /^\s*(let\s*[^;\/\\]*)\s*$/i and let_cmd($1), last SWITCH;
1111 $input =~ /^\s*exec(?:ute)?\s*(.*)\s*$/i and exec_cmd($1), last SWITCH;
1112 $input =~ /^\s*\\d\s*$/ and show('show objects', 'table'), last SWITCH;
1113 $input =~ /^\s*\\dt\s*$/ and show('show tables', 'table'), last SWITCH;
1114 $input =~ /^\s*\\di\s*$/ and show('show indexes', 'table'), last SWITCH;
1115 $input =~ /^\s*\\ds\s*$/ and show('show sequences', 'table'), last SWITCH;
1116 $input =~ /^\s*\\dv\s*$/ and show('show views', 'table'), last SWITCH;
1117 $input =~ /^\s*\\df\s*$/ and show('show functions', 'table'), last SWITCH;
1119 # Global commands allowed any time (even in the middle of queries)
1120 $input =~ /^\s*quit\s*$/i and quit(0), last SWITCH;
1121 $input =~ /^\s*exit\s*$/i and quit(0), last SWITCH;
1122 $input =~ /^\s*\\q\s*$/i and quit(0), last SWITCH;
1123 $input =~ /^\s*\\l\s*$/i and show_qbuffer(), last SWITCH;
1124 $input =~ /^\s*\\p\s*$/i and show_qbuffer(), last SWITCH;
1125 $input =~ /^\s*l\s*$/i and show_qbuffer(), last SWITCH;
1126 $input =~ /^\s*list\s*$/i and show_qbuffer(), last SWITCH;
1127 $input =~ /^\s*\\c\s*$/i and $nprompt = clear_qbuffer(), last SWITCH;
1128 $input =~ /^\s*clear\s*$/i and $nprompt = clear_qbuffer(), last SWITCH;
1129 $input =~ /^\s*clear buffer\s*$/i and $nprompt=clear_qbuffer(), last SWITCH;
1130 $input =~ /^\s*\\e\s*(.*)$/i and $nprompt = edit($1), last SWITCH;
1131 $input =~ /^\s*edit\s*(.*)$/i and $nprompt = edit($1), last SWITCH;
1132 $input =~ /^\s*rem(?:ark)?/i and $input = '', last SWITCH;
1133 $input =~ /[^\s]/ and $nprompt = parse_input($input) || last, last SWITCH;
1135 # default
1136 $nprompt = $prompt if ($nprompt eq ''); # use last prompt if nothing caught (blank line)
1138 if(!$opt_batch && $term->ReadLine eq "Term::ReadLine::Gnu" && $input =~ /[^\s]/ &&
1139 $input ne $last_history) {
1140 if (!$opt_batch && $add_to_history) {
1141 $term->AddHistory($input);
1144 $last_history = $input;
1145 return($nprompt);
1148 sub parse_input {
1149 my($input) = @_;
1150 debugmsg(3, "parse_input called", @_);
1151 # this takes input and parses it. It looks for single quotes (') and double
1152 # quotes (") and presents prompts accordingly. It also looks for query
1153 # terminators, such as semicolon (;), forward-slash (/) and back-slash-g (\g).
1154 # If it finds a query terminator, then it pushes any text onto the query
1155 # buffer ($qbuffer) and then passes the entire query buffer, as well as the
1156 # format type, determined by the terminator type, to the query() function. It
1157 # also wipes out the qbuffer at this time.
1159 # It returns a prompt (like 'SQL> ' or ' -> ') if successfull, 0 otherwise
1161 # now we need to check for a terminator, if we're not inquotes
1162 while( $input =~ m/
1164 ['"] # match quotes
1165 | # or
1166 ; # the ';' terminator
1167 | # or
1168 ^\s*\/\s*$ # the slash terminator at end of string
1169 | # or
1170 \\[GgsSi] # one of the complex terminators
1171 | # or
1172 (?:^|\s+)create\s+ # create
1173 | # or
1174 (?:^|\s+)function\s+ # function
1175 | # or
1176 (?:^|\s+)package\s+ # package
1177 | # or
1178 (?:^|\s+)package\s+body\s+ # package body
1179 | # or
1180 (?:^|\s+)procedure\s+ # procedure
1181 | # or
1182 (?:^|\s+)trigger\s+ # trigger
1183 | # or
1184 (?:^|\s+)declare\s+ # declare
1185 | # or
1186 (?:^|\s+)begin\s+ # begin
1187 | # or
1188 \/\* # start of multiline comment
1189 | # or
1190 \*\/ # end of multiline comment
1191 )/gix )
1194 my($pre, $match, $post) = ($`, $1, $');
1195 # PREMATCH, MATCH, POSTMATCH
1196 debugmsg(1, "parse: [$pre] [$match] [$post]");
1198 if( ($match eq '\'' || $match eq '"')) {
1199 if(!$quote || $quote eq $match) {
1200 $inquotes = ($inquotes ? 0 : 1);
1201 if($inquotes) {
1202 $quote = $match;
1203 } else {
1204 undef($quote);
1207 } elsif($match =~ /create/ix) {
1208 $increate = 1;
1209 } elsif(!$increate &&
1210 $match =~ /function|package|package\s+body|procedure|trigger/ix)
1212 # do nothing if we're not in a create statement
1213 } elsif(($match =~ /declare|begin/ix) ||
1214 ($increate && $match =~ /function|package|package\s+body|procedure|trigger/ix))
1216 $inplsqlblock = 1;
1217 } elsif($match =~ /^\/\*/) {
1218 $incomment = 1;
1219 } elsif($match =~ /^\*\//) {
1220 $incomment = 0;
1221 } elsif(!$inquotes && !$incomment && $match !~ /^--/ &&
1222 ($match =~ /^\s*\/\s*$/ || !$inplsqlblock))
1224 $qbuffer .= $pre;
1225 debugmsg(4,"qbuffer IN: [$qbuffer]");
1226 my $terminator = $match;
1227 $post =~ / (\d*) # Match num_rows right after terminitor
1228 \s* # Optional whitespace
1229 (?: #
1230 ( >{1,2}|<|\| ) # Match redirection operators
1231 \s* # Optional whitespace
1232 ( .* ) # The redirector (include rest of line)
1233 )? # Match 0 or 1
1234 \s* # Optional whitespace
1235 (.*) # Catch everything else
1236 $ # End-Of-Line
1238 debugmsg(3,"1: [$1] 2: [$2] 3: [$3] 4: [$4]");
1240 my($num_rows,$op,$op_text,$extra) = ($1,$2,$3,$4);
1242 if($extra =~ /--.*$/) {
1243 undef $extra;
1246 # check that Text::CSV_XS is installed if a < redirection was given
1247 if($op eq '<' && $notextcsv) {
1248 soft_err("You must install Text::CSV_XS from CPAN to use this feature");
1249 return(0);
1252 # deduce the format from the terminator type
1253 my $format;
1255 $fbuffer = $terminator;
1257 if($terminator eq ';' || $terminator =~ /^\/\s*$/) {
1258 $format = 'table';
1259 } elsif($terminator eq '\g') {
1260 $format = 'list';
1261 } elsif($terminator eq '\G') {
1262 $format = 'list_aligned';
1263 } elsif($terminator eq '\s') {
1264 $format = 'csv';
1265 } elsif($terminator eq '\S') {
1266 $format = 'csv_no_header';
1267 } elsif($terminator eq '\i') {
1268 $format = 'sql';
1270 $num_rows ||= 0;
1272 debugmsg(4,"fbuffer: [$fbuffer]\n");
1274 # if there is nothing in the buffer, then we assume that the user just
1275 # wants to reexecute the last query, which we have saved in $last_qbuffer
1276 my($use_buffer, $copy_buffer);
1277 if($qbuffer) {
1278 $use_buffer = $qbuffer;
1279 $copy_buffer = 1;
1280 } elsif($last_qbuffer) {
1281 $use_buffer = $last_qbuffer;
1282 $copy_buffer = 0;
1283 } else {
1284 $use_buffer = undef;
1285 $copy_buffer = 0;
1288 if($use_buffer) {
1289 if($op eq '<') {
1290 my $count = 0;
1291 my($max_lines, @params, $max_lines_save, @querybench,
1292 $rows_affected, $success_code);
1293 my $result_output = 1;
1294 push(@querybench, get_bench());
1295 print STDERR "\n";
1296 while(($max_lines, @params) = get_csv_file($op, $op_text)) {
1297 $max_lines_save = $max_lines;
1298 print statusline($count, $max_lines);
1300 my @res = query( $use_buffer, $format,
1301 {num_rows => $num_rows, op => $op, op_text => $op_text,
1302 result_output => 0}, @params);
1304 debugmsg(3, "res: [@res]");
1306 unless(@res) {
1307 print "Error in line " . ($count + 1) . " of file '$op_text'\n";
1308 $result_output = 0;
1309 close_csv();
1310 last;
1313 $rows_affected += $res[0];
1314 $success_code = $res[1];
1315 $count++;
1317 push(@querybench, get_bench());
1319 if($result_output) {
1320 print "\r\e[K";
1322 if(!$opt_batch) {
1323 print STDERR format_affected($rows_affected, $success_code);
1324 if($opt_bench || $conf{extended_benchmarks}) {
1325 print STDERR "\n\n";
1326 print STDERR ('-' x 80);
1327 print STDERR "\n";
1328 output_benchmark("Query: ", @querybench, "\n");
1329 } else {
1330 output_benchmark(" (", @querybench, ")");
1331 print STDERR "\n";
1333 print STDERR "\n";
1336 } else {
1337 query($use_buffer, $format, {num_rows => $num_rows, op => $op,
1338 op_text => $op_text});
1341 if($copy_buffer) {
1342 # copy the current qbuffer to old_qbuffer
1343 $last_qbuffer = $qbuffer;
1344 $last_fbuffer = $fbuffer;
1346 } else {
1347 query_err('Query', 'No current query in buffer');
1350 undef($qbuffer);
1351 undef($fbuffer);
1352 $inplsqlblock = 0;
1353 $increate = 0;
1355 if($extra) {
1356 return(parse_input($extra));
1357 } else {
1358 # return a 'new' prompt
1359 return(get_prompt());
1364 $qbuffer .= $input . "\n";
1366 debugmsg(4,"qbuffer: [$qbuffer], input: [$input]");
1368 if($inquotes) {
1369 return(get_prompt($quote));
1370 } elsif($incomment) {
1371 return(get_prompt('DOC'));
1372 } else {
1373 return(get_prompt('-'));
1377 sub get_csv_file {
1378 my($op, $op_text) = @_;
1379 debugmsg(3, "get_csv_file called", @_);
1381 my @ret = ();
1383 unless($csv_max_lines) {
1384 ($op_text) = glob($op_text);
1385 debugmsg(3, "Opening file '$op_text' for line counting");
1386 open(CSV, $op_text) || do{
1387 query_err('redirect',"Cannot open file '$op_text' for reading: $!");
1388 return();
1390 while(<CSV>) {
1391 $csv_max_lines++;
1393 close(CSV);
1396 unless($csv_filehandle_open) {
1397 ($op_text) = glob($op_text);
1398 debugmsg(3, "Opening file '$op_text' for input");
1399 open(CSV, $op_text) || do{
1400 query_err('redirect',"Cannot open file '$op_text' for reading: $!");
1401 return();
1403 $csv_filehandle_open = 1;
1406 my $line = <CSV>;
1407 while(defined($line) && $line =~ /^\s*$/) {
1408 $line = <CSV>;
1411 unless($line) {
1412 close_csv();
1413 return();
1416 debugmsg(3, "read in CSV line", $line);
1418 my @fields;
1419 if($csv->parse($line)) {
1420 @fields = $csv->fields();
1421 debugmsg(3, "got CVS fields", @fields);
1422 } else {
1423 wrn("Parse of CSV file failed on argument, skipping to next: "
1424 . $csv->error_input());
1425 return(get_csv_file($op, $op_text));
1428 return($csv_max_lines, @fields);
1431 sub close_csv {
1432 close(CSV) || lerr("Could not close CSV filehandle: $!");
1433 $csv_filehandle_open = 0;
1434 $csv_max_lines = 0;
1437 sub connect_cmd {
1438 my($arg) = @_;
1439 debugmsg(3, "connect_cmd called", @_);
1441 unless($arg) {
1442 wrn("Invalid connect syntax. See help");
1443 return(0);
1446 my($ora_session_mode, $username, $password, $connect_string) = parse_logon_string($arg);
1448 my $new_dbh = db_connect(0, $ora_session_mode, $username, $password, $connect_string);
1449 if (not $new_dbh) {
1450 warn "failed to make new connection as $username to $connect_string: $DBI::errstr\n";
1451 warn "keeping old connection\n";
1452 return;
1455 if (defined $dbh) {
1456 commit_on_exit();
1457 $dbh->disconnect()
1458 or warn "failed to disconnect old connection - switching anyway\n";
1461 $dbh = $new_dbh;
1462 $connected = 1;
1465 sub disconnect_cmd {
1466 debugmsg(3, "disconnect_cmd called", @_);
1468 if ($connected) {
1469 print "Closing last connection...\n";
1470 commit_on_exit();
1472 $dbh->disconnect() if (defined $dbh);
1473 $connected = 0;
1474 } else {
1475 print "Not connected.\n";
1479 sub commit_cmd {
1480 debugmsg(3, "commit_cmd called", @_);
1481 # this just called commit
1483 if(defined $dbh) {
1484 if($dbh->{AutoCommit}) {
1485 wrn("commit ineffective with AutoCommit enabled");
1486 } else {
1487 if ($dbh->commit()) {
1488 print "Transaction committed\n";
1490 else {
1491 warn "Commit failed: $DBI::errstr\n";
1494 } else {
1495 print "No connection\n";
1499 sub rollback_cmd {
1500 debugmsg(3, "rollback_cmd called", @_);
1501 # this just called commit
1503 if(defined $dbh) {
1504 if($dbh->{AutoCommit}) {
1505 wrn("rollback ineffective with AutoCommit enabled");
1506 } else {
1507 if ($dbh->rollback()) {
1508 print "Transaction rolled back\n";
1510 else {
1511 warn "Rollback failed: $DBI::errstr\n";
1514 } else {
1515 print "No connection\n";
1519 sub exec_cmd {
1520 my($sqlstr) = @_;
1521 debugmsg(3, "exec_cmd called", @_);
1522 # Wrap the statement in BEGIN/END and execute
1524 $sqlstr = qq(
1525 BEGIN
1526 $sqlstr
1527 END;
1530 query($sqlstr, 'table');
1533 sub edit {
1534 my($filename) = @_;
1535 debugmsg(3, "edit called", @_);
1536 # This writes the current qbuffer to a file then opens up an editor on that
1537 # file... when the editor returns, we read in the file and overwrite the
1538 # qbuffer with it. If there is nothing in the qbuffer, and there is
1539 # something in the last_qbuffer, then we use the last_qbuffer. If nothing
1540 # is in either, then we just open the editor with a blank file.
1542 my $passed_file = 1 if $filename;
1543 my $filecontents;
1544 my $prompt = get_prompt();
1546 debugmsg(2, "passed_file: [$passed_file]");
1548 if($qbuffer) {
1549 debugmsg(2, "Using current qbuffer for contents");
1550 $filecontents = $qbuffer;
1551 } elsif($last_qbuffer) {
1552 debugmsg(2, "Using last_qbuffer for contents");
1553 $filecontents = $last_qbuffer . $last_fbuffer;
1554 } else {
1555 debugmsg(2, "Using blank contents");
1556 $filecontents = "";
1559 debugmsg(3, "filecontents: [$filecontents]");
1561 # determine the tmp directory
1562 my $tmpdir;
1563 if($ENV{TMP}) {
1564 $tmpdir = $ENV{TMP};
1565 } elsif($ENV{TEMP}) {
1566 $tmpdir = $ENV{TEMP};
1567 } elsif(-d "/tmp") {
1568 $tmpdir = "/tmp";
1569 } else {
1570 $tmpdir = ".";
1573 # determine the preferred editor
1574 my $editor;
1575 if($ENV{EDITOR}) {
1576 $editor = $ENV{EDITOR};
1577 } else {
1578 $editor = "vi";
1581 # create the filename, if not given one
1582 $filename ||= "$tmpdir/yasql_" . int(rand(1000)) . "_$$.sql";
1584 # expand the filename
1585 ($filename) = glob($filename);
1587 debugmsg(1, "Editing $filename with $editor");
1589 # check for file existance. If it exists, then we open it up but don't
1590 # write the buffer to it
1591 my $file_exists;
1592 if($passed_file) {
1593 # if the file was passed, then check for it's existance
1594 if(-e $filename) {
1595 # The file was found
1596 $file_exists = 1;
1597 } elsif(-e "$filename.sql") {
1598 # the file was found with a .sql extension
1599 $filename = "$filename.sql";
1600 $file_exists = 1;
1601 } else {
1602 wrn("$filename was not found, creating new file, which will not be ".
1603 "deleted");
1605 } else {
1606 # no file was specified, so just write to the the temp file, and we
1607 # don't care if it exists, since there's no way another process could
1608 # write to the same file at the same time since we use the PID in the
1609 # filename.
1610 my $ret = open(TMPFILE, ">$filename");
1611 if(!$ret) { #if file was NOT opened successfully
1612 wrn("Could not write to $filename: $!");
1613 } else {
1614 print TMPFILE $filecontents;
1615 close(TMPFILE);
1619 # now spawn the editor
1620 my($ret, @filecontents);
1621 debugmsg(2, "Executing $editor $filename");
1622 $ret = system($editor, "$filename");
1623 if($ret) {
1624 debugmsg(2, "Executing env $editor $filename");
1625 $ret = system("env", $editor, "$filename");
1627 if($ret) {
1628 debugmsg(2, "Executing `which $editor` $filename");
1629 $ret = system("`which $editor`", "$filename");
1632 if($ret) { #if the editor or system returned a positive return value
1633 wrn("Editor exited with $ret: $!");
1634 } else {
1635 # read in the tmp file and apply it's contents to the buffer
1636 my $ret = open(TMPFILE, "$filename");
1637 if(!$ret) { # if file was NOT opened successfully
1638 wrn("Could not read $filename: $!");
1639 } else {
1640 # delete our qbuffer and reset the inquotes var
1641 $qbuffer = "";
1642 $inquotes = 0;
1643 $increate = 0;
1644 $inplsqlblock = 0;
1645 $incomment = 0;
1646 while(<TMPFILE>) {
1647 push(@filecontents, $_);
1649 close(TMPFILE);
1653 if(@filecontents) {
1654 print "\n";
1655 print join('', @filecontents);
1656 print "\n";
1658 foreach my $line (@filecontents) {
1659 # chomp off newlines
1660 chomp($line);
1662 last if $sigintcaught;
1663 # now send it in to process_input
1664 # and don't add lines of the script to command history
1665 $prompt = process_input($line, '', 0);
1669 unless($passed_file) {
1670 # delete the tmp file
1671 debugmsg(1, "Deleting $filename");
1672 unlink("$filename") ||
1673 wrn("Could not unlink $filename: $!");
1676 return($prompt);
1679 sub run_script {
1680 my($input) = @_;
1681 debugmsg(3, "run_script called", @_);
1682 # This reads in the given script and executes it's lines as if they were typed
1683 # in directly. It will NOT erase the current buffer before it runs. It
1684 # will append the contents of the file to the current buffer, basicly
1686 my $prompt;
1688 # parse input
1689 $input =~ /^\@(.*)$/;
1690 my $file = $1;
1691 ($file) = glob($file);
1692 debugmsg(2, "globbed [$file]");
1694 my $first_char = substr($file, 0, 1);
1695 unless($first_char eq '/' or $first_char eq '.') {
1696 foreach my $path ('.', @sqlpath) {
1697 if(-e "$path/$file") {
1698 $file = "$path/$file";
1699 last;
1700 } elsif(-e "$path/$file.sql") {
1701 $file = "$path/$file.sql";
1702 last;
1706 debugmsg(2, "Found [$file]");
1708 # read in the tmp file and apply it's contents to the buffer
1709 my $ret = open(SCRIPT, $file);
1710 if(!$ret) { # if file was NOT opened successfully
1711 wrn("Could not read $file: $!");
1712 $prompt = get_prompt();
1713 } else {
1714 # read in the script
1715 while(<SCRIPT>) {
1716 # chomp off newlines
1717 chomp;
1719 last if $sigintcaught;
1721 # now send it in to process_input
1722 # and don't add lines of the script to command history
1723 $prompt = process_input($_, '', 0);
1725 close(SCRIPT);
1728 return($prompt);
1731 sub show_qbuffer {
1732 debugmsg(3, "show_qbuffer called", @_);
1733 # This outputs the current buffer
1735 #print "\nBuffer:\n";
1736 if($qbuffer) {
1737 print $qbuffer;
1738 } else {
1739 print STDERR "Buffer empty";
1741 print "\n";
1744 sub clear_qbuffer {
1745 debugmsg(3, "clear_qbuffer called", @_);
1746 # This clears the current buffer
1748 $qbuffer = '';
1749 $inquotes = 0;
1750 $inplsqlblock = 0;
1751 $increate = 0;
1752 $incomment = 0;
1753 print "Buffer cleared\n";
1754 return(get_prompt());
1757 sub debug_toggle {
1758 my($debuglevel) = @_;
1759 debugmsg(3, "debug_toggle called", @_);
1760 # If nothing is passed, then debugging is turned off if on, on if off. If
1761 # a number is passed, then we explicitly set debugging to that number
1764 if(length($debuglevel) > 0) {
1765 unless($debuglevel =~ /^\d+$/) {
1766 wrn('Debug level must be an integer');
1767 return(1);
1770 $opt_debug = $debuglevel;
1771 } else {
1772 if($opt_debug) {
1773 $opt_debug = 0;
1774 } else {
1775 $opt_debug = 1;
1778 $opt_debug > 3 ? DBI->trace(1) : DBI->trace(0);
1779 print "** debug is now " . ($opt_debug ? "level $opt_debug" : 'off') . "\n";
1782 sub autocommit_toggle {
1783 debugmsg(3, "autocommit_toggle called", @_);
1784 # autocommit is turned off if on on if off
1786 if($dbh->{AutoCommit}) {
1787 $dbh->{AutoCommit} = 0;
1788 } else {
1789 $dbh->{AutoCommit} = 1;
1792 print "AutoCommit is now " . ($dbh->{AutoCommit} ? 'on' : 'off') . "\n";
1795 sub show_all_query {
1796 my ( $select, $order_by, $format, $opts, $static_where , $option, $option_key, @values ) = @_;
1797 debugmsg(3, "show_all_query called");
1798 my $where = ' where ';
1799 if ( $static_where ) {
1800 $where = ' where '. $static_where . ' ';
1803 if ( $option eq 'like' ){
1804 my $sqlstr = $select . $where;
1805 $sqlstr .= ' and ' if ( $static_where );
1806 $sqlstr .= $option_key ." like ? " . $order_by;
1808 query($sqlstr , $format, $opts, @values );
1809 }else{
1810 my $sqlstr = $select;
1811 $sqlstr .= $where if ($static_where);
1812 $sqlstr .= $order_by;
1814 query($sqlstr , $format, $opts );
1819 sub show {
1820 my($input, $format, $num_rows, $op, $op_text) = @_;
1821 debugmsg(3, "show called", @_);
1822 # Can 'show thing'. Possible things:
1823 # tables - outputs all of the tables that the current user owns
1824 # sequences - outputs all of the sequences that the current user owns
1826 # Can also 'show thing on table'. Possible things:
1827 # constraints - Shows constraints on the 'table', like Check, Primary Key,
1828 # Unique, and Foreign Key
1829 # indexes - Shows indexes on the 'table'
1830 # triggers - Shows triggers on the 'table'
1832 # convert to lowercase for comparison operations
1833 $input = lc($input);
1835 # drop trailing whitespaces
1836 ($input = $input) =~ s/( +)$//;
1838 # parse the input to find out what 'thing' has been requested
1839 if($input =~ /^\s*show\s+([a-zA-Z0-9_\$\#\s]+)\s+(?:on|for)\s+([a-zA-Z0-9_\$\#]+)/) {
1840 # this is a thing on a table
1841 if($1 eq 'indexes') {
1842 my $sqlstr;
1843 if($dbversion >= 8) {
1844 $sqlstr = q{
1845 select ai.index_name "Index Name",
1846 ai.index_type "Type",
1847 ai.uniqueness "Unique?",
1848 aic.column_name "Column Name"
1849 from all_indexes ai, all_ind_columns aic
1850 where ai.index_name = aic.index_name
1851 and ai.table_owner = aic.table_owner
1852 and ai.table_name = ?
1853 and ai.table_owner = ?
1854 order by ai.index_name, aic.column_position
1856 } else {
1857 $sqlstr = q{
1858 select ai.index_name "Index Name",
1859 ai.uniqueness "Unique?",
1860 aic.column_name "Column Name"
1861 from all_indexes ai, all_ind_columns aic
1862 where ai.index_name = aic.index_name
1863 and ai.table_owner = aic.table_owner
1864 and ai.table_name = ?
1865 and ai.table_owner = ?
1866 order by ai.index_name, aic.column_position
1869 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1870 op_text => $op_text}, uc($2), uc($dbuser));
1871 } elsif($1 eq 'constraints') {
1872 my $sqlstr = q{
1873 select constraint_name "Constraint Name",
1874 decode(constraint_type,
1875 'C', 'Check',
1876 'P', 'Primary Key',
1877 'R', 'Foreign Key',
1878 'U', 'Unique',
1879 '') "Type",
1880 search_condition "Search Condition"
1881 from all_constraints
1882 where table_name = ?
1883 and owner = ?
1884 order by constraint_name
1886 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1887 op_text => $op_text}, uc($2), uc($dbuser));
1888 } elsif($1 eq 'keys') {
1889 my $sqlstr = q{
1890 select ac.constraint_name "Name",
1891 decode(ac.constraint_type,
1892 'R', 'Foreign Key',
1893 'U', 'Unique',
1894 'P', 'Primary Key',
1895 ac.constraint_type) "Type",
1896 ac.table_name "Table Name",
1897 acc.column_name "Column",
1898 r_ac.table_name "Parent Table",
1899 r_acc.column_name "Parent Column"
1900 from all_constraints ac, all_cons_columns acc,
1901 all_constraints r_ac, all_cons_columns r_acc
1902 where ac.constraint_name = acc.constraint_name
1903 and ac.owner = acc.owner
1904 and ac.constraint_type in ('R','U','P')
1905 and ac.r_constraint_name = r_ac.constraint_name(+)
1906 and r_ac.constraint_name = r_acc.constraint_name(+)
1907 and r_ac.owner = r_acc.owner(+)
1908 and ac.table_name = ?
1909 and ac.owner = ?
1910 order by ac.constraint_name, acc.position
1912 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1913 op_text => $op_text}, uc($2), uc($dbuser));
1914 } elsif($1 eq 'checks') {
1915 my $sqlstr = q{
1916 select ac.constraint_name "Name",
1917 decode(ac.constraint_type,
1918 'C', 'Check',
1919 ac.constraint_type) "Type",
1920 ac.table_name "Table Name",
1921 ac.search_condition "Search Condition"
1922 from all_constraints ac
1923 where ac.table_name = ?
1924 and ac.constraint_type = 'C'
1925 and ac.owner = ?
1926 order by ac.constraint_name
1928 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1929 op_text => $op_text}, uc($2), uc($dbuser));
1930 } elsif($1 eq 'triggers') {
1931 my $sqlstr = q{
1932 select trigger_name "Trigger Name",
1933 trigger_type "Type",
1934 when_clause "When",
1935 triggering_event "Event"
1936 from all_triggers
1937 where table_name = ?
1938 and owner = ?
1940 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1941 op_text => $op_text}, uc($2), uc($dbuser));
1942 } elsif($1 eq 'query') {
1943 my $sqlstr = q{
1944 select count(*) from all_mviews where mview_name = ? and owner = ?
1946 my $is_mview = $dbh->selectrow_array($sqlstr, undef, uc($2), uc($dbuser));
1947 if($is_mview) {
1948 $sqlstr = q{
1949 select query
1950 from all_mviews
1951 where mview_name = ?
1952 and owner = ?
1954 } else {
1955 $sqlstr = q{
1956 select text
1957 from all_views
1958 where view_name = ?
1959 and owner = ?
1962 my $prev_LongReadLen = $dbh->{LongReadLen};
1963 $dbh->{LongReadLen} = 8000;
1964 query($sqlstr, 'single_output', {num_rows => $num_rows, op => $op,
1965 op_text => $op_text}, uc($2), uc($dbuser));
1966 $dbh->{LongReadLen} = $prev_LongReadLen;
1967 } elsif($1 eq 'deps') {
1968 my $table = $2;
1969 my $sqlstr = q{
1970 select
1971 column_name "Column Name"
1972 , type "Type"
1973 , tablett || '(' || pk || ')' "Reference"
1974 , constraint_name "Constraint"
1975 from (
1976 select
1977 a.owner,
1978 a.table_name,
1979 b.column_name,
1980 c.owner || '.' || c.table_name tablett,
1981 d.column_name pk,
1982 a.constraint_name,
1983 'parent ->' type
1984 from all_constraints a,
1985 all_cons_columns b,
1986 all_constraints c,
1987 all_cons_columns d
1988 where a.constraint_name = b.constraint_name
1989 and a.r_constraint_name is not null
1990 and a.r_constraint_name=c.constraint_name
1991 and c.constraint_name=d.constraint_name
1992 and a.owner = b.owner and c.owner = d.owner
1993 UNION
1994 SELECT
1995 a.owner,
1996 a.table_name parent_table,
1997 b.column_name,
1998 c.owner || '.' || c.table_name tablett,
1999 d.column_name pk,
2000 c.constraint_name,
2001 'child <-' as type
2002 FROM all_constraints a,
2003 all_cons_columns b,
2004 all_constraints c,
2005 all_cons_columns d
2006 WHERE a.constraint_name = b.constraint_name
2007 AND a.constraint_name = c.r_constraint_name
2008 AND c.constraint_name = d.constraint_name
2009 and a.owner = b.owner and c.owner = d.owner
2010 ) where table_name like ?
2011 and owner like ?
2012 ORDER BY 2,1,3,4
2014 query($sqlstr, 'table', {num_rows => $num_rows, op => $op,
2015 op_text => $op_text}, uc($table), uc($dbuser));
2016 } elsif($1 eq 'ddl') {
2017 my $table = $2;
2018 my $sqlstr = q{
2019 SELECT DBMS_METADATA.GET_DDL('TABLE', ?, ?) FROM dual
2020 union all
2021 SELECT DBMS_METADATA.GET_DEPENDENT_DDL('INDEX', ?, ?) FROM dual
2022 union all
2023 SELECT DBMS_METADATA.GET_DEPENDENT_DDL ('COMMENT', ?, ?) FROM dual
2024 union all
2025 SELECT DBMS_METADATA.GET_DEPENDENT_DDL('TRIGGER', ?, ?) FROM dual
2027 my $prev_LongReadLen = $dbh->{LongReadLen};
2028 $dbh->{LongReadLen} = 16_000;
2029 query($sqlstr, 'quiet-list', {num_rows => $num_rows, op => $op, op_text => $op_text}
2030 ,uc($table)
2031 ,uc($dbuser)
2032 ,uc($table)
2033 ,uc($dbuser)
2034 ,uc($table)
2035 ,uc($dbuser)
2036 ,uc($table)
2037 ,uc($dbuser)
2039 $dbh->{LongReadLen} = $prev_LongReadLen;
2040 } else {
2041 query_err("show", "Unsupported show type", $input);
2043 } elsif($input =~ /^\s*show\s+all\s+([a-zA-Z0-9_\$\#]+)\s*([a-zA-Z0-9_\'\$\#\%\s]*)$/) {
2044 my $object = $1;
2045 my $rest = $2;
2046 my $option = '';
2047 my $option_value = '';
2048 my $opts = {
2049 num_rows => $num_rows
2050 ,op => $op
2051 ,op_text => $op_text
2053 # Workaround for materialized views
2054 if ($object eq 'materialized' and $2 =~ /views\s*([a-zA-Z0-9_\$\#\%\s]*)/ ){
2055 $object = 'materialized views';
2056 $rest = $1;
2059 if ($rest =~ /\s*(\w+)\s+[']?([a-zA-Z0-9_\$\#\%]+)[']?/){
2060 $option = lc($1);
2061 $option_value = uc($2);
2064 if($object eq 'tables') {
2066 show_all_query(
2067 q{select table_name "Table Name", 'TABLE' "Type", owner "Owner" from all_tables }
2068 ,q{ order by table_name }
2069 ,$format
2070 ,$opts
2071 ,q{}
2072 ,$option
2073 ,q{table_name}
2074 ,$option_value
2077 } elsif($object eq 'views') {
2079 show_all_query(
2080 q{select view_name "View Name", 'VIEW' "Type", owner "Owner" from all_views }
2081 ,q{ order by view_name }
2082 ,$format
2083 ,$opts
2084 ,q{}
2085 ,$option
2086 ,q{view_name}
2087 ,$option_value
2090 } elsif($object eq 'objects') {
2092 show_all_query(
2093 q{select object_name "Object Name", object_type "Type", owner "Owner" from all_objects }
2094 ,q{ order by object_name }
2095 ,$format
2096 ,$opts
2097 ,q{}
2098 ,$option
2099 ,q{object_name}
2100 ,$option_value
2103 } elsif($object eq 'sequences') {
2105 show_all_query(
2106 q{select sequence_name "Sequence Name", 'SEQUENCE' "Type", sequence_owner "Owner" from all_sequences }
2107 ,q{ order by sequence_name }
2108 ,$format
2109 ,$opts
2110 ,q{}
2111 ,$option
2112 ,q{sequence_name}
2113 ,$option_value
2116 } elsif($object eq 'clusters') {
2118 show_all_query(
2119 q{select cluster_name "Cluster Name", 'CLUSTER' "Type", owner "Owner" from all_clusters}
2120 ,q{ order by cluster_name }
2121 ,$format
2122 ,$opts
2123 ,q{}
2124 ,$option
2125 ,q{cluster_name}
2126 ,$option_value
2129 } elsif($object eq 'dimensions') {
2131 show_all_query(
2132 q{select dimension_name "Dimension Name", 'DIMENSION' "Type", owner "Owner" from all_dimensions}
2133 ,q{ order by dimension_name }
2134 ,$format
2135 ,$opts
2136 ,q{}
2137 ,$option
2138 ,q{dimension_name}
2139 ,$option_value
2142 } elsif($object eq 'functions') {
2144 show_all_query(
2145 q{select distinct name "Function Name", 'FUNCTION' "Type", owner "Owner" from all_source}
2146 ,q{ order by name }
2147 ,$format
2148 ,$opts
2149 ,q{type = 'FUNCTION'}
2150 ,$option
2151 ,q{name}
2152 ,$option_value
2155 } elsif($object eq 'procedures') {
2157 show_all_query(
2158 q{select distinct name "Procedure Name", 'PROCEDURE' "Type", owner "Owner" from all_source}
2159 ,q{ order by name }
2160 ,$format
2161 ,$opts
2162 ,q{type = 'PROCEDURE'}
2163 ,$option
2164 ,q{name}
2165 ,$option_value
2168 } elsif($object eq 'packages') {
2170 show_all_query(
2171 q{select distinct name "Package Name", 'PACKAGES' "Type", owner "Owner" from all_source}
2172 ,q{ order by name }
2173 ,$format
2174 ,$opts
2175 ,q{type = 'PACKAGE'}
2176 ,$option
2177 ,q{name}
2178 ,$option_value
2181 } elsif($object eq 'indexes') {
2183 show_all_query(
2184 q{select index_name "Index Name", 'INDEXES' "Type", owner "Owner" from all_indexes}
2185 ,q{ order by index_name }
2186 ,$format
2187 ,$opts
2188 ,q{}
2189 ,$option
2190 ,q{index_name}
2191 ,$option_value
2194 } elsif($object eq 'indextypes') {
2196 show_all_query(
2197 q{select indextype_name "Indextype Name", 'INDEXTYPE' "Type", owner "Owner" from all_indextypes}
2198 ,q{ order by indextype_name }
2199 ,$format
2200 ,$opts
2201 ,q{}
2202 ,$option
2203 ,q{indextype_name}
2204 ,$option_value
2207 } elsif($object eq 'libraries') {
2209 show_all_query(
2210 q{select library_name "library Name", 'LIBRARY' "Type", owner "Owner" from all_libraries}
2211 ,q{ order by library_name }
2212 ,$format
2213 ,$opts
2214 ,q{}
2215 ,$option
2216 ,q{library_name}
2217 ,$option_value
2220 } elsif($object eq 'materialized views') {
2222 show_all_query(
2223 q{select mview_name "Materialized View Name", 'MATERIALIZED VIEW' "Type", owner "Owner" from all_mviews}
2224 ,q{ order by mview_name }
2225 ,$format
2226 ,$opts
2227 ,q{}
2228 ,$option
2229 ,q{mview_name}
2230 ,$option_value
2233 } elsif($object eq 'snapshots') {
2235 show_all_query(
2236 q{select name "Snapshot Name", 'SNAPSHOT' "Type", owner "Owner" from all_snapshots}
2237 ,q{ order by name }
2238 ,$format
2239 ,$opts
2240 ,q{}
2241 ,$option
2242 ,q{name}
2243 ,$option_value
2246 } elsif($object eq 'synonyms') {
2248 show_all_query(
2249 q{select synonym_name "Synonym Name", 'SYNONYM' "Type", owner "Owner" from all_synonyms}
2250 ,q{ order by synonym_name }
2251 ,$format
2252 ,$opts
2253 ,q{}
2254 ,$option
2255 ,q{synonym_name}
2256 ,$option_value
2260 } elsif($object eq 'triggers') {
2262 show_all_query(
2263 q{select trigger_name "Trigger Name", 'TRIGGER' "Type", owner "Owner" from all_triggers}
2264 ,q{ order by trigger_name }
2265 ,$format
2266 ,$opts
2267 ,q{}
2268 ,$option
2269 ,q{trigger_name}
2270 ,$option_value
2273 } elsif($object eq 'waits') {
2274 my $sqlstr = q{
2275 select vs.username "Username",
2276 vs.osuser "OS User",
2277 vsw.sid "SID",
2278 vsw.event "Event",
2279 decode(vsw.wait_time, -2, ' Unknown',
2280 to_char(vsw.seconds_in_wait,'999,999,999,999'))
2281 "Seconds Waiting"
2282 from v$session_wait vsw,
2283 v$session vs
2284 where vsw.sid = vs.sid
2285 order by vsw.wait_time desc, vsw.seconds_in_wait desc, vsw.sid
2287 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2288 op_text => $op_text});
2290 } elsif( $object eq 'constraints' ){
2292 my $sqlstr = q{
2293 select
2294 CONSTRAINT_NAME "Constraint Name"
2295 ,decode(constraint_type,
2296 'C', 'Check',
2297 'P', 'Primary Key',
2298 'R', 'Foreign Key',
2299 'U', 'Unique',
2300 '') "Type"
2301 ,TABLE_NAME "Table Name"
2302 ,INDEX_NAME "Index Name"
2303 ,STATUS "Status"
2304 from all_constraints
2306 show_all_query(
2307 $sqlstr
2308 ,q{ order by CONSTRAINT_NAME }
2309 ,$format
2310 ,$opts
2311 ,q{}
2312 ,$option
2313 ,q{CONSTRAINT_NAME}
2314 ,$option_value
2317 } else {
2318 query_err("show", "Unsupported show type", $input);
2320 } elsif($input =~ /^\s*show\s+([a-zA-Z0-9_\$\#\s]+)\s*$/) {
2321 if($1 eq 'tables') {
2322 my $sqlstr = q{
2323 select table_name "Table Name", 'TABLE' "Type", sys.login_user() "Owner"
2324 from user_tables
2325 order by table_name
2327 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2328 op_text => $op_text});
2329 } elsif($1 eq 'views') {
2330 my $sqlstr = q{
2331 select view_name "View Name", 'VIEW' "Type", sys.login_user() "Owner"
2332 from user_views
2333 order by view_name
2335 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2336 op_text => $op_text});
2337 } elsif($1 eq 'objects') {
2338 my $sqlstr = q{
2339 select object_name "Object Name", object_type "Type", sys.login_user() "Owner"
2340 from user_objects
2341 order by object_name
2343 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2344 op_text => $op_text});
2345 } elsif($1 eq 'sequences') {
2346 my $sqlstr = q{
2347 select sequence_name "Sequence Name", 'SEQUENCE' "Type", sys.login_user() "Owner"
2348 from user_sequences
2349 order by sequence_name
2351 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2352 op_text => $op_text});
2353 } elsif($1 eq 'clusters') {
2354 my $sqlstr = q{
2355 select cluster_name "Cluster Name", 'CLUSTER' "Type", sys.login_user() "Owner"
2356 from user_clusters
2357 order by cluster_name
2359 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2360 op_text => $op_text});
2361 } elsif($1 eq 'dimensions') {
2362 my $sqlstr = q{
2363 select dimension_name "Dimension Name", 'DIMENSION' "Type", sys.login_user() "Owner"
2364 from user_dimensions
2365 order by dimension_name
2367 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2368 op_text => $op_text});
2369 } elsif($1 eq 'functions') {
2370 my $sqlstr = q{
2371 select distinct name "Function Name", 'FUNCTION' "Type", sys.login_user() "Owner"
2372 from user_source
2373 where type = 'FUNCTION'
2374 order by name
2376 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2377 op_text => $op_text});
2378 } elsif($1 eq 'procedures') {
2379 my $sqlstr = q{
2380 select distinct name "Procedure Name", 'PROCEDURE' "Type", sys.login_user() "Owner"
2381 from user_source
2382 where type = 'PROCEDURE'
2383 order by name
2385 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2386 op_text => $op_text});
2387 } elsif($1 eq 'packages') {
2388 my $sqlstr = q{
2389 select distinct name "Package Name", 'PACKAGES' "Type", sys.login_user() "Owner"
2390 from user_source
2391 where type = 'PACKAGE'
2392 order by name
2394 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2395 op_text => $op_text});
2396 } elsif($1 eq 'indexes') {
2397 my $sqlstr = q{
2398 select index_name "Index Name", 'INDEXES' "Type", sys.login_user() "Owner"
2399 from user_indexes
2400 order by index_name
2402 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2403 op_text => $op_text});
2404 } elsif($1 eq 'indextypes') {
2405 my $sqlstr = q{
2406 select indextype_name "Indextype Name", 'INDEXTYPE' "Type", sys.login_user() "Owner"
2407 from user_indextypes
2408 order by indextype_name
2410 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2411 op_text => $op_text});
2412 } elsif($1 eq 'libraries') {
2413 my $sqlstr = q{
2414 select library_name "library Name", 'LIBRARY' "Type", sys.login_user() "Owner"
2415 from user_libraries
2416 order by library_name
2418 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2419 op_text => $op_text});
2420 } elsif($1 eq 'materialized views') {
2421 my $sqlstr = q{
2422 select mview_name "Materialized View Name", 'MATERIALIZED VIEW' "Type", sys.login_user() "Owner"
2423 from user_mviews
2424 order by mview_name
2426 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2427 op_text => $op_text});
2428 } elsif($1 eq 'snapshots') {
2429 my $sqlstr = q{
2430 select name "Snapshot Name", 'SNAPSHOT' "Type", sys.login_user() "Owner"
2431 from user_snapshots
2432 order by name
2434 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2435 op_text => $op_text});
2436 } elsif($1 eq 'synonyms') {
2437 my $sqlstr = q{
2438 select synonym_name "Synonym Name", 'SYNONYM' "Type", sys.login_user() "Owner"
2439 from user_synonyms
2440 order by synonym_name
2442 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2443 op_text => $op_text});
2444 } elsif($1 eq 'triggers') {
2445 my $sqlstr = q{
2446 select trigger_name "Trigger Name", 'TRIGGER' "Type", sys.login_user() "Owner"
2447 from user_triggers
2448 order by trigger_name
2450 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2451 op_text => $op_text});
2452 } elsif($1 eq 'processes') {
2453 my $sqlstr = q{
2454 select sid,
2455 vs.username "User",
2456 vs.status "Status",
2457 vs.schemaname "Schema",
2458 vs.osuser || '@' || vs.machine "From",
2459 to_char(vs.logon_time, 'Mon DD YYYY HH:MI:SS') "Logon Time",
2460 aa.name "Command"
2461 from v$session vs, audit_actions aa
2462 where vs.command = aa.action
2463 and username is not null
2465 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2466 op_text => $op_text});
2467 } elsif($1 eq 'locks') {
2468 my $sqlstr = q{
2469 SELECT
2470 s.username "Username"
2471 ,s.osuser || '@' || s.MACHINE "User@Machine"
2472 ,s.PROGRAM "Program"
2473 ,s.sid sid
2474 ,l.LMODE || ':' ||
2475 decode(L.LMODE,
2476 1,'No Lock',
2477 2,'Row Share',
2478 3,'Row Exclusive',
2479 4,'Share',
2480 5,'Share Row Exclusive',
2481 6,'Exclusive','NONE') "LMode"
2482 ,l.type || ':' ||
2483 decode(l.type,
2484 'BL','Buffer hash table instance lock',
2485 'CF',' Control file schema global enqueue lock',
2486 'CI','Cross-instance function invocation instance lock',
2487 'CS','Control file schema global enqueue lock',
2488 'CU','Cursor bind lock',
2489 'DF','Data file instance lock',
2490 'DL','Direct loader parallel index create',
2491 'DM','Mount/startup db primary/secondary instance lock',
2492 'DR','Distributed recovery process lock',
2493 'DX','Distributed transaction entry lock',
2494 'FI','SGA open-file information lock',
2495 'FS','File set lock',
2496 'HW','Space management operations on a specific segment lock',
2497 'IN','Instance number lock',
2498 'IR','Instance recovery serialization global enqueue lock',
2499 'IS','Instance state lock',
2500 'IV','Library cache invalidation instance lock',
2501 'JQ','Job queue lock',
2502 'KK','Thread kick lock',
2503 'MB','Master buffer hash table instance lock',
2504 'MM','Mount definition gloabal enqueue lock',
2505 'MR','Media recovery lock',
2506 'PF','Password file lock',
2507 'PI','Parallel operation lock',
2508 'PR','Process startup lock',
2509 'PS','Parallel operation lock',
2510 'RE','USE_ROW_ENQUEUE enforcement lock',
2511 'RT','Redo thread global enqueue lock',
2512 'RW','Row wait enqueue lock',
2513 'SC','System commit number instance lock',
2514 'SH','System commit number high water mark enqueue lock',
2515 'SM','SMON lock',
2516 'SN','Sequence number instance lock',
2517 'SQ','Sequence number enqueue lock',
2518 'SS','Sort segment lock',
2519 'ST','Space transaction enqueue lock',
2520 'SV','Sequence number value lock',
2521 'TA','Generic enqueue lock',
2522 'TD','DDL enqueue lock',
2523 'TE','Extend-segment enqueue lock',
2524 'TM','DML enqueue lock',
2525 'TT','Temporary table enqueue lock',
2526 'TX','Transaction enqueue lock',
2527 'UL','User supplied lock',
2528 'UN','User name lock',
2529 'US','Undo segment DDL lock',
2530 'WL','Being-written redo log instance lock',
2531 'WS','Write-atomic-log-switch global enqueue lock') "Lock Type"
2532 ,CASE
2533 WHEN l.type = 'TM' THEN (
2534 SELECT OBJECT_TYPE || ' : ' || OWNER || '.' || OBJECT_NAME
2535 FROM ALL_OBJECTS
2536 where object_id = l.id1
2538 WHEN l.type = 'TX' AND l.BLOCK = 1 THEN (
2539 SELECT
2540 'Blocked Sessions: ' || max(substr(SYS_CONNECT_BY_PATH(SID, ','),2)) SID
2541 FROM (
2542 SELECT
2543 l2.id1,
2544 l2.id2,
2545 l2.SID,
2546 row_number() OVER (Partition by l2.id1 order by l2.id1 ) seq
2547 FROM
2548 v$lock l2
2549 WHERE
2550 l2.block = 0
2552 where id1 = l.id1
2553 and id2 = l.id2
2554 start with
2555 seq=1
2556 connect by prior
2557 seq+1=seq
2558 and prior
2559 id1=id1
2560 GROUP BY id1
2562 WHEN l.type = 'TX' AND l.REQUEST > 0 THEN (
2563 SELECT
2564 'Wait for Session: ' || SID
2565 FROM V$LOCK l2
2566 WHERE l.id1 = l2.id1
2567 and l.id2 = l2.id2
2568 and block = 1
2570 ELSE 'unknown'
2571 END AS "Locked object / Lock Info"
2572 ,l.CTIME
2573 FROM V$LOCK l
2574 LEFT JOIN V$SESSION s ON l.SID = s.SID
2575 WHERE l.type <> 'MR' AND s.type <> 'BACKGROUND'
2577 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2578 op_text => $op_text});
2580 } elsif($1 eq 'waits') {
2581 my $sqlstr = q{
2582 select vs.username "Username",
2583 vs.osuser "OS User",
2584 vsw.sid "SID",
2585 vsw.event "Event",
2586 decode(vsw.wait_time, -2, ' Unknown',
2587 to_char(vsw.seconds_in_wait,'999,999,999,999'))
2588 "Seconds Waiting"
2589 from v$session_wait vsw,
2590 v$session vs
2591 where vsw.sid = vs.sid
2592 and vs.status = 'ACTIVE'
2593 and vs.username is not null
2594 order by vsw.wait_time desc, vsw.seconds_in_wait desc, vsw.sid
2596 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2597 op_text => $op_text});
2598 } elsif($1 eq 'plan') {
2599 # This following query is Copyright (c) Oracle Corporation 1998, 1999. All Rights Reserved.
2600 my $sqlstr = q{
2601 select '| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |' as "Plan Table" from dual
2602 union all
2603 select '--------------------------------------------------------------------------------' from dual
2604 union all
2605 select rpad('| '||substr(lpad(' ',1*(level-1)) ||operation||
2606 decode(options, null,'',' '||options), 1, 27), 28, ' ')||'|'||
2607 rpad(substr(object_name||' ',1, 9), 10, ' ')||'|'||
2608 lpad(decode(cardinality,null,' ',
2609 decode(sign(cardinality-1000), -1, cardinality||' ',
2610 decode(sign(cardinality-1000000), -1, trunc(cardinality/1000)||'K',
2611 decode(sign(cardinality-1000000000), -1, trunc(cardinality/1000000)||'M',
2612 trunc(cardinality/1000000000)||'G')))), 7, ' ') || '|' ||
2613 lpad(decode(bytes,null,' ',
2614 decode(sign(bytes-1024), -1, bytes||' ',
2615 decode(sign(bytes-1048576), -1, trunc(bytes/1024)||'K',
2616 decode(sign(bytes-1073741824), -1, trunc(bytes/1048576)||'M',
2617 trunc(bytes/1073741824)||'G')))), 6, ' ') || '|' ||
2618 lpad(decode(cost,null,' ',
2619 decode(sign(cost-10000000), -1, cost||' ',
2620 decode(sign(cost-1000000000), -1, trunc(cost/1000000)||'M',
2621 trunc(cost/1000000000)||'G'))), 8, ' ') || '|' ||
2622 lpad(decode(partition_start, 'ROW LOCATION', 'ROWID',
2623 decode(partition_start, 'KEY', 'KEY', decode(partition_start,
2624 'KEY(INLIST)', 'KEY(I)', decode(substr(partition_start, 1, 6),
2625 'NUMBER', substr(substr(partition_start, 8, 10), 1,
2626 length(substr(partition_start, 8, 10))-1),
2627 decode(partition_start,null,' ',partition_start)))))||' ', 7, ' ')|| '|' ||
2628 lpad(decode(partition_stop, 'ROW LOCATION', 'ROW L',
2629 decode(partition_stop, 'KEY', 'KEY', decode(partition_stop,
2630 'KEY(INLIST)', 'KEY(I)', decode(substr(partition_stop, 1, 6),
2631 'NUMBER', substr(substr(partition_stop, 8, 10), 1,
2632 length(substr(partition_stop, 8, 10))-1),
2633 decode(partition_stop,null,' ',partition_stop)))))||' ', 7, ' ')||'|' as "Explain plan"
2634 from plan_table
2635 start with id=0 and timestamp = (select max(timestamp) from plan_table where id=0)
2636 connect by prior id = parent_id
2637 and prior nvl(statement_id, ' ') = nvl(statement_id, ' ')
2638 and prior timestamp <= timestamp
2639 union all
2640 select '--------------------------------------------------------------------------------' from dual
2642 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2643 op_text => $op_text});
2644 } elsif($1 eq 'errors') {
2645 my $err = $dbh->func( 'plsql_errstr' );
2646 if($err) {
2647 print "\n$err\n\n";
2648 } else {
2649 print "\nNo errors.\n\n";
2651 } elsif($1 eq 'users') {
2652 my $sqlstr = q{
2653 select username, user_id, created
2654 from all_users
2656 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2657 op_text => $op_text});
2658 } elsif($1 eq 'user') {
2659 my $sqlstr = q{
2660 select user from dual
2662 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2663 op_text => $op_text});
2664 } elsif($1 eq 'uid') {
2665 my $sqlstr = q{
2666 select uid from dual
2668 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2669 op_text => $op_text});
2670 } elsif(($1 eq 'database links') || ($1 eq 'dblinks')) {
2671 my $sqlstr = q{
2672 select db_link, host, owner from all_db_links
2674 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2675 op_text => $op_text});
2676 } else {
2677 query_err("show", "Unsupported show type", $input);
2679 } else {
2680 query_err("show", "Unsupported show type", $input);
2686 sub describe {
2687 my($input, $format, $nosynonym, $num_rows, $op, $op_text) = @_;
2688 debugmsg(3, "describe called", @_);
2689 # This describes a table, view, sequence, or synonym by listing it's
2690 # columns and their attributes
2692 # convert to lowercase for comparison operations
2693 $input = lc($input);
2695 # make sure we're still connected to the database
2696 unless(ping()) {
2697 wrn("Database connection died");
2698 db_reconnect();
2701 # parse the query to find the table that was requested to be described
2702 if($input =~ /^\s*desc\w*\s*([a-zA-Z0-9_\$\#\.\@]+)/) {
2703 my $object = $1;
2704 my $sqlstr;
2705 my $type;
2706 my @ret;
2708 my $schema;
2709 my $dblink;
2710 if($object =~ /^([a-zA-Z0-9_\$\#]+)\.([a-zA-Z0-9_\$\#]+)\@([a-zA-Z0-9_\$\#]+)$/) {
2711 $schema = $1;
2712 $object = $2;
2713 $dblink = "\@$3";
2714 } elsif($object =~ /^([a-zA-Z0-9_\$\#]+)\@([a-zA-Z0-9_\$\#]+)$/) {
2715 $schema = $dbuser;
2716 $object = $1;
2717 $dblink = "\@$2";
2718 } elsif($object =~ /^([a-zA-Z0-9_\$\#]+)\.([a-zA-Z0-9_\$\#]+)$/) {
2719 $schema = $1;
2720 $object = $2;
2721 } else {
2722 $schema = $dbuser;
2725 debugmsg(1,"schema: [$schema] object: [$object] dblink: [$dblink]");
2727 if($conf{fast_describe}) {
2728 if(my $sth = $dbh->prepare("select * from $schema.$object$dblink")) {
2729 my $fields = $sth->{NAME};
2730 my $types = $sth->{TYPE};
2731 my $type_info = $dbh->type_info($types->[0]);
2732 my $precision = $sth->{PRECISION};
2733 my $scale = $sth->{SCALE};
2734 my $nullable = $sth->{NULLABLE};
2736 debugmsg(4, "fields: [" . join(',', @$fields) . "]");
2737 debugmsg(4, "types: [" . join(',', @$types) . "]");
2738 debugmsg(4, "type_info: [" . Dumper($type_info) . "]");
2739 debugmsg(4, "precision: [" . join(',', @$precision) . "]");
2740 debugmsg(4, "scale: [" . join(',', @$scale) . "]");
2741 debugmsg(4, "nullable: [" . join(',', @$nullable) . "]");
2743 # Assemble a multidiminsional array of the output
2744 my @desc;
2745 for(my $i = 0; $i < @$fields; $i++) {
2746 my ($name, $null, $type);
2747 $name = $fields->[$i];
2748 $null = ($nullable->[$i] ? 'NULL' : 'NOT NULL');
2749 my $type_info = $dbh->type_info($types->[$i]);
2750 $type = $type_info->{'TYPE_NAME'};
2751 # convert DECIMAL to NUMBER for our purposes (some kind of DBD kludge)
2752 $type = 'NUMBER' if $type eq 'DECIMAL';
2753 if( $type eq 'VARCHAR2' || $type eq 'NVARCHAR2' ||
2754 $type eq 'CHAR' || $type eq 'NCHAR' || $type eq 'RAW' )
2756 $type .= "($precision->[$i])";
2757 } elsif($type eq 'NUMBER' && ($scale->[$i] || $precision->[$i] < 38))
2759 $type .= "($precision->[$i],$scale->[$i])";
2761 push(@desc, [$name, $null, $type]);
2764 # figure max column sizes we'll need
2765 my @widths = (4,5,4);
2766 for(my $i = 0; $i < @desc; $i++) {
2767 for(my $j = 0; $j < @{$desc[0]}; $j++) {
2768 if(length($desc[$i][$j]) > $widths[$j]) {
2769 $widths[$j] = length($desc[$i][$j]);
2774 # open the redirection file
2775 if($op && $op eq '>' || $op eq '>>') {
2776 ($op_text) = glob($op_text);
2777 debugmsg(3, "Opening file '$op_text' for output redirection using [$op]");
2778 open(FOUT, $op . $op_text) || do query_err('redirect',"Cannot open file '$op_text' for writing: $!", '');
2779 } elsif($op eq '|') {
2780 debugmsg(3, "Opening pipe to '$op_text' for output redirection");
2781 open(FOUT, $op . $op_text) || do query_err('pipe',"Cannot open pipe '$op_text': $!", '');
2782 } else {
2783 open(FOUT, ">&STDOUT");
2786 if($opt_headers) {
2787 # Print headers
2788 print FOUT "\n";
2789 print FOUT sprintf("%-$widths[0]s", 'Name')
2790 . ' '
2791 . sprintf("%-$widths[1]s", 'Null?')
2792 . ' '
2793 . sprintf("%-$widths[2]s", 'Type')
2794 . "\n";
2795 print FOUT '-' x $widths[0]
2796 . ' '
2797 . '-' x $widths[1]
2798 . ' '
2799 . '-' x $widths[2]
2800 . "\n";
2802 for(my $i = 0; $i < @desc; $i++) {
2803 for(my $j = 0; $j < @{$desc[$i]}; $j++) {
2804 print FOUT ' ' if $j > 0;
2805 print FOUT sprintf("%-$widths[$j]s", $desc[$i][$j]);
2807 print FOUT "\n";
2809 print FOUT "\n";
2811 close(FOUT);
2813 return();
2817 # look in all_constraints for the object first. This is because oracle
2818 # stores information about primary keys in the all_objects table as "index"s
2819 # but it doesn't have foreign keys or constraints. So we want to match
2820 # there here first
2822 # now look in all_objects
2823 my $all_object_cols = 'object_type,owner,object_name,'
2824 . 'object_id,created,last_ddl_time,'
2825 . 'timestamp,status';
2827 @ret = $dbh->selectrow_array(
2828 "select $all_object_cols from all_objects where object_name = ? "
2829 ."and owner = ?"
2830 .($nosynonym ? " and object_type != 'SYNONYM'" : ""),
2831 undef, uc($object), uc($schema)
2832 ) or
2833 @ret = $dbh->selectrow_array(
2834 "select $all_object_cols from all_objects where object_name = ? "
2835 ."and owner = 'PUBLIC'"
2836 .($nosynonym ? " and object_type != 'SYNONYM'" : ""),
2837 undef, uc($object)
2840 unless(@ret) {
2841 @ret = $dbh->selectrow_array(
2842 "select constraint_type, constraint_name from all_constraints where "
2843 ."constraint_name = ?",
2844 undef, uc($object)
2848 if($ret[0] eq 'INDEX') {
2849 # Check if this 'index' is really a primary key and is in the
2850 # all_constraints table
2852 my @temp_ret = $dbh->selectrow_array(
2853 "select constraint_type, constraint_name from all_constraints where "
2854 ."constraint_name = ?",
2855 undef, uc($object)
2858 @ret = @temp_ret if @temp_ret;
2861 $type = $ret[0];
2862 debugmsg(1,"type: [$type] ret: [@ret]");
2864 if($type eq 'SYNONYM') {
2865 # Find what this is a synonym to, then recursively call this function
2866 # again to describe whatever it points to
2867 my($table_name, $table_owner) = $dbh->selectrow_array(
2868 'select table_name, table_owner from all_synonyms '
2869 .'where synonym_name = ? and owner = ?',
2870 undef, uc($ret[2]), uc($ret[1])
2873 describe("desc $table_owner.$table_name", $format, 1);
2874 } elsif($type eq 'SEQUENCE') {
2875 my $sqlstr = q{
2876 select sequence_name "Name",
2877 min_value "Min",
2878 max_value "Max",
2879 increment_by "Inc",
2880 cycle_flag "Cycle",
2881 order_flag "Order",
2882 last_number "Last"
2883 from all_sequences
2884 where sequence_name = ?
2885 and sequence_owner = ?
2887 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2888 op_text => $op_text}, uc($ret[2]), uc($ret[1]));
2889 } elsif($type eq 'TABLE' || $type eq 'VIEW' || $type eq 'TABLE PARTITION') {
2890 my $sqlstr = q{
2891 select column_name "Name",
2892 decode(nullable,
2893 'N','NOT NULL'
2894 ) "Null?",
2895 decode(data_type,
2896 'VARCHAR2','VARCHAR2(' || TO_CHAR(data_length) || ')',
2897 'NVARCHAR2','NVARCHAR2(' || TO_CHAR(data_length) || ')',
2898 'CHAR','CHAR(' || TO_CHAR(data_length) || ')',
2899 'NCHAR','NCHAR(' || TO_CHAR(data_length) || ')',
2900 'NUMBER',
2901 decode(data_precision,
2902 NULL, 'NUMBER',
2903 'NUMBER(' || TO_CHAR(data_precision)
2904 || ',' || TO_CHAR(data_scale) || ')'
2906 'FLOAT',
2907 decode(data_precision,
2908 NULL, 'FLOAT', 'FLOAT(' || TO_CHAR(data_precision) || ')'
2910 'DATE','DATE',
2911 'LONG','LONG',
2912 'LONG RAW','LONG RAW',
2913 'RAW','RAW(' || TO_CHAR(data_length) || ')',
2914 'MLSLABEL','MLSLABEL',
2915 'ROWID','ROWID',
2916 'CLOB','CLOB',
2917 'NCLOB','NCLOB',
2918 'BLOB','BLOB',
2919 'BFILE','BFILE',
2920 data_type || ' ???'
2921 ) "Type",
2922 data_default "Default"
2923 from all_tab_columns atc
2924 where table_name = ?
2925 and owner = ?
2926 order by column_id
2928 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2929 op_text => $op_text}, uc($ret[2]), uc($ret[1]));
2930 } elsif($type eq 'R') {
2931 my $sqlstr = q{
2932 select ac.constraint_name "Name",
2933 decode(ac.constraint_type,
2934 'R', 'Foreign Key',
2935 'C', 'Check',
2936 'U', 'Unique',
2937 'P', 'Primary Key',
2938 ac.constraint_type) "Type",
2939 ac.table_name "Table Name",
2940 acc.column_name "Column Name",
2941 r_ac.table_name "Parent Table",
2942 r_acc.column_name "Parent Column",
2943 ac.delete_rule "Delete Rule"
2944 from all_constraints ac, all_cons_columns acc,
2945 all_constraints r_ac, all_cons_columns r_acc
2946 where ac.constraint_name = acc.constraint_name
2947 and ac.owner = acc.owner
2948 and ac.r_constraint_name = r_ac.constraint_name
2949 and r_ac.constraint_name = r_acc.constraint_name
2950 and r_ac.owner = r_acc.owner
2951 and ac.constraint_type = 'R'
2952 and ac.constraint_name = ?
2953 and ac.owner = ?
2954 order by ac.constraint_name, acc.position
2956 query($sqlstr, 'list_aligned', {num_rows => $num_rows, op => $op,
2957 op_text => $op_text}, uc($ret[1]),
2958 uc($schema));
2959 } elsif($type eq 'P' || $type eq 'U') {
2960 my $sqlstr = q{
2961 select ac.constraint_name "Name",
2962 decode(ac.constraint_type,
2963 'R', 'Foreign Key',
2964 'C', 'Check',
2965 'U', 'Unique',
2966 'P', 'Primary Key',
2967 ac.constraint_type) "Type",
2968 ac.table_name "Table Name",
2969 acc.column_name "Column Name"
2970 from all_constraints ac, all_cons_columns acc
2971 where ac.constraint_name = acc.constraint_name
2972 and ac.owner = acc.owner
2973 and ac.constraint_name = ?
2974 and ac.owner = ?
2975 order by ac.constraint_name, acc.position
2977 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2978 op_text => $op_text}, uc($ret[1]), uc($schema));
2979 } elsif($type eq 'C') {
2980 my $sqlstr = q{
2981 select ac.constraint_name "Name",
2982 decode(ac.constraint_type,
2983 'R', 'Foreign Key',
2984 'C', 'Check',
2985 'U', 'Unique',
2986 'P', 'Primary Key',
2987 ac.constraint_type) "Type",
2988 ac.table_name "Table Name",
2989 ac.search_condition "Search Condition"
2990 from all_constraints ac
2991 where ac.constraint_name = ?
2992 order by ac.constraint_name
2994 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2995 op_text => $op_text}, uc($ret[1]));
2996 } elsif($type eq 'INDEX') {
2997 my $sqlstr = q{
2998 select ai.index_name "Index Name",
2999 ai.index_type "Type",
3000 ai.table_name "Table Name",
3001 ai.uniqueness "Unique?",
3002 aic.column_name "Column Name"
3003 from all_indexes ai, all_ind_columns aic
3004 where ai.index_name = aic.index_name(+)
3005 and ai.table_owner = aic.table_owner(+)
3006 and ai.index_name = ?
3007 and ai.table_owner = ?
3008 order by aic.column_position
3010 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
3011 op_text => $op_text}, uc($ret[2]), uc($schema));
3012 } elsif($type eq 'TRIGGER') {
3013 my $sqlstr = q{
3014 select trigger_name "Trigger Name",
3015 trigger_type "Type",
3016 triggering_event "Event",
3017 table_name "Table",
3018 when_clause "When",
3019 description "Description",
3020 trigger_body "Body"
3021 from all_triggers
3022 where trigger_name = ?
3024 query($sqlstr, 'list_aligned', {num_rows => $num_rows, op => $op,
3025 op_text => $op_text}, uc($ret[2]));
3026 } elsif($type eq 'PACKAGE') {
3027 wrn("Not implemented (yet)");
3028 } elsif($type eq 'PROCEDURE') {
3029 wrn("Not implemented (yet)");
3030 } elsif($type eq 'CLUSTER') {
3031 wrn("Not implemented (yet)");
3032 } elsif($type eq 'TRIGGER') {
3033 wrn("Not implemented (yet)");
3034 } else {
3035 query_err('describe', "Object $object not found");
3039 sub let_cmd {
3040 my($input) = @_;
3041 debugmsg(3, "let_cmd called", @_);
3042 my @book_keys = qw/sql_query_in_error auto_complete edit_history fast_describe complete_objects complete_tables extended_complete_list extended_benchmarks column_wildcards complete_columns auto_commit commit_on_exit command_complete_list long_trunc_ok/;
3044 if ($input =~ /^\s*let\s*(\w+)?\s*/i ){
3045 my @print_keys = keys %conf;
3046 @print_keys = grep(/$1/,@print_keys) if ($1);
3048 foreach my $key ( @print_keys ){
3049 my $print_conf = $conf{$key};
3050 $print_conf = ($conf{$key}) ? 'On' : 'Off' if ( grep(/$key/,@book_keys) ) ;
3051 printf("%25s : %1s\n",$key,$print_conf);
3053 }else{
3054 print "usage let <config name>\n";
3057 sub set_cmd {
3058 my($input) = @_;
3059 debugmsg(3, "set_cmd called", @_);
3060 # This mimics SQL*Plus set commands, or ignores them completely. For those
3061 # that are not supported, we do nothing at all, but return silently.
3063 if($input =~ /^\s*set\s+serverout(?:put)?\s+(on|off)(?:\s+size\s+(\d+))?/i) {
3064 if(lc($1) eq 'on') {
3065 my $size = $2 || 1_000_000;
3066 debugmsg(2, "calling dbms_output_enable($size)");
3067 $dbh->func( $size, 'dbms_output_enable' )
3068 or warn "dbms_output_enable($size) failed: $DBI::errstr\n";
3069 $set{serveroutput} = 1;
3070 debugmsg(2, "serveroutput set to $set{serveroutput}");
3071 } else {
3072 $set{serveroutput} = 0;
3073 debugmsg(2, "serveroutput set to $set{serveroutput}");
3075 }elsif($input =~ /^\s*set\s+(long_read_len|LongReadLen)\s+(\d+)/i){
3076 debugmsg(2, "long_read_len/LongReadLen set to $2");
3077 $dbh->{LongReadLen} = $2;
3078 }elsif($input =~ /^\s*set\s+fast_describe\s+(on|off)/i){
3079 $conf{fast_describe} = (lc($1) eq 'on') ? 1 : 0;
3080 print "fast_describe is now " . ($conf{fast_describe} ? 'on' : 'off') . "\n";
3082 }elsif($input =~ /^\s*set\s+(\w+)\s*/ ){
3083 print "Can't set option $1\n";
3087 sub query {
3088 my($sqlstr, $format, $opts, @bind_vars) = @_;
3089 debugmsg(3, "query called", @_);
3090 # this runs the provided query and calls format_display to display the results
3092 my $num_rows = $opts->{num_rows};
3093 my $op = $opts->{op};
3094 my $op_text = $opts->{op_text};
3095 my $result_output = ( exists $opts->{result_output}
3096 ? $opts->{result_output}
3100 my(@totalbench, @querybench, @formatbench);
3102 # Look for special query types, such as "show" and "desc" that we handle
3103 # and don't send to the database at all, since they're not really valid SQL.
3105 my ($rows_affected, $success_code);
3107 if($sqlstr =~ /^\s*desc/i) {
3108 describe($sqlstr, $format, undef, $num_rows, $op, $op_text);
3109 } elsif($sqlstr =~ /^\s*show/i) {
3110 show($sqlstr, $format, $num_rows, $op, $op_text);
3111 } else {
3112 $running_query = 1;
3114 # make sure we're still connected to the database
3115 unless(ping()) {
3116 wrn("Database connection died");
3117 db_reconnect();
3120 $sqlstr = wildcard_expand($sqlstr) if $conf{column_wildcards};
3122 # send the query on to the database
3123 push(@totalbench, get_bench()) if !$conf{extended_benchmarks};
3124 push(@querybench, get_bench()) if $conf{extended_benchmarks};
3125 debugmsg(3, "preparing", $sqlstr);
3126 my $sth = $dbh->prepare($sqlstr);
3127 unless($sth) {
3128 my $err = $DBI::errstr;
3129 $err =~ s/ \(DBD ERROR\: OCIStmtExecute\/Describe\)//;
3131 if ($err =~ m/DBD ERROR\:/) {
3132 my $indicator_offset = $DBI::errstr;
3133 $indicator_offset =~ s/(.*)(at\ char\ )(\d+)(\ .*)/$3/;
3134 if ($indicator_offset > 0) {
3135 my $i = 0;
3136 print $sqlstr, "\n";
3137 for ($i=0;$i<$indicator_offset;++$i) {
3138 print " ";
3140 print "*\n";
3144 # Output message if serveroutput is on
3145 if($set{serveroutput}) {
3146 debugmsg(3, "Calling dmbs_output_get");
3147 my @output = $dbh->func( 'dbms_output_get' );
3148 print join("\n", @output) . "\n";
3150 query_err('prepare', $err, $sqlstr), setup_sigs(), return();
3152 debugmsg(2, "sth: [$sth]");
3154 $cursth = $sth;
3156 finish_query($sth), return() if $sigintcaught; #pseudo sig handle
3158 my $ret;
3159 eval {
3160 debugmsg(3, "executing", $sqlstr);
3161 $ret = $sth->execute(@bind_vars);
3163 debugmsg(3, "ret:", $ret, "\@:", $@, "\$DBI::errstr:", $DBI::errstr);
3164 if(!$ret) {
3165 my $eval_error = $@;
3166 $eval_error =~ s/at \(eval \d+\) line \d+, <\S+> line \d+\.//;
3167 my $err = $DBI::errstr;
3168 $err =~ s/ \(DBD ERROR: OCIStmtExecute\)//;
3169 # Output message is serveroutput is on
3170 if($set{serveroutput}) {
3171 debugmsg(3, "Calling dmbs_output_get");
3172 my @output = $dbh->func( 'dbms_output_get' );
3173 print join("\n", @output) . "\n";
3175 my $errstr = ($eval_error ? $eval_error : $err);
3176 query_err('execute', $errstr, $sqlstr);
3177 setup_sigs();
3178 return();
3181 if($DBI::errstr =~ /^ORA-24344/) {
3182 print "\nWarning: Procedure created with compilation errors.\n\n";
3183 setup_sigs();
3184 return();
3187 push(@querybench, get_bench()) if $conf{extended_benchmarks};
3189 finish_query($sth), return() if $sigintcaught; #pseudo sig handle
3191 debugmsg(1, "rows returned: [" . $sth->rows() . "]");
3193 # open the redirection file
3194 if($op && $op eq '>' || $op eq '>>') {
3195 ($op_text) = glob($op_text);
3196 debugmsg(3, "Opening file '$op_text' for output redirection using [$op]");
3197 open(FOUT, $op . $op_text) || do{
3198 query_err('redirect',"Cannot open file '$op_text' for writing: $!",
3199 $sqlstr);
3200 finish_query($sth);
3201 return();
3203 } elsif($op eq '|') {
3204 debugmsg(3, "Opening pipe to '$op_text' for output redirection");
3205 open(FOUT, $op . $op_text) || do{
3206 query_err('pipe',"Cannot open pipe '$op_text': $!", $sqlstr);
3207 finish_query($sth);
3208 return();
3210 } else {
3211 open(FOUT, ">&STDOUT");
3214 # Output message is serveroutput is on
3215 if($set{serveroutput}) {
3216 debugmsg(3, "Calling dmbs_output_get");
3217 my @output = $dbh->func( 'dbms_output_get' );
3218 print join("\n", @output) . "\n";
3221 # Determine type and output accordingly
3222 if($sqlstr =~ /^\s*declare|begin/i) {
3223 print STDERR "\nPL/SQL procedure successfully completed.\n\n";
3224 } else {
3225 push(@formatbench, get_bench()) if $conf{extended_benchmarks};
3226 ($rows_affected, $success_code) = format_output($sth, $format, $num_rows,
3227 $sqlstr, $op, $op_text)
3228 or finish_query($sth), return();
3229 push(@formatbench, get_bench()) if $conf{extended_benchmarks};
3230 push(@totalbench, get_bench()) if !$conf{extended_benchmarks};
3232 finish_query($sth), return() if $sigintcaught; #pseudo sig handle
3234 # output format_affected
3235 if($result_output) {
3236 if(!$opt_batch) {
3237 print STDERR "\n" . format_affected($rows_affected, $success_code);
3240 if(!$opt_batch) {
3241 if($opt_bench || $conf{extended_benchmarks}) {
3242 print STDERR "\n\n";
3243 print STDERR ('-' x 80);
3244 print STDERR "\n";
3245 output_benchmark("Query: ", @querybench, "\n");
3246 output_benchmark("Format:", @formatbench, "\n");
3247 } else {
3248 output_benchmark(" (", @totalbench, ")");
3249 print STDERR "\n";
3251 print STDERR "\n";
3256 close(FOUT);
3258 finish_query($sth);
3260 undef($sth);
3261 undef($cursth);
3264 return($rows_affected, $success_code);
3267 sub wildcard_expand {
3268 my($sql) = @_;
3269 debugmsg(3, "wildcard_expand called", @_);
3271 my $newsql = $sql;
3272 my $fromstuff;
3273 my $wheregrouporder = $sql;
3274 $wheregrouporder =~ s/.*(where|order|group).*/\1/;
3275 if ($wheregrouporder eq $sql) {
3276 $wheregrouporder = "";
3278 ($sql,$fromstuff) = split(/order|group|where/i,$sql,2);
3279 if ($sql =~ /^select\s+(.+?)\*\s+from\s+(.+)/i) {
3280 debugmsg(1, "Match made: ($1) ($2)");
3281 my $wildcardstring = uc($1);
3282 my $tablename = uc($2);
3283 my @tlist = split(/,/,$tablename);
3284 my $tablelist = "";
3285 my %column_prefix;
3286 foreach my $table (@tlist) {
3287 $table =~ s/^ *//;
3288 $table =~ s/([^ ]+)\s+(.*)/\1/;
3289 $column_prefix{$table} = $2 ? $2 : $table;
3290 $tablelist .= ($tablelist ? "," : "") . $table;
3292 $tablelist =~ s/,/' or table_name='/g;
3293 my $qstr = "select table_name||'.'||column_name from all_tab_columns where (table_name='$tablelist') and column_name like '$wildcardstring%' escape '\\'";
3294 debugmsg(1, "qstr: [$qstr]");
3295 my $sth = $dbh->prepare($qstr);
3296 $sth->execute();
3297 setup_sigs();
3298 my $colname;
3299 my $collist;
3300 while ( ($colname) = $sth->fetchrow_array() ) {
3301 foreach my $table (keys %column_prefix) {
3302 $colname =~ s/$table\./$column_prefix{$table}\./;
3303 $colname =~ s/ //g;
3305 $collist .= ($collist ? "," : "") . $colname;
3307 $collist = $collist ? $collist : "*";
3308 $newsql = "select " . $collist . " from " . $tablename . " "
3309 . $wheregrouporder . " " . $fromstuff;
3310 debugmsg(1, "newsql: [$newsql]");
3312 $newsql;
3315 sub finish_query {
3316 my($sth) = @_;
3317 # This just finishes the query and cleans up the state info
3319 $sth->finish;
3320 undef($cursth);
3321 $running_query = 0;
3322 setup_sigs();
3325 sub get_bench {
3326 debugmsg(3, "get_bench called", @_);
3327 # returns benchmark info
3329 my($benchmark, $hires);
3330 $benchmark = new Benchmark;
3332 if($nohires) {
3333 $hires = time;
3334 } else {
3335 # use an eval to keep perl from syntax checking it unless we have the
3336 # Time::HiRes module loaded
3337 eval q{
3338 $hires = [gettimeofday]
3342 return($benchmark, $hires);
3345 sub output_benchmark {
3346 my($string, $bstart, $hrstart, $bend, $hrend, $string2) = @_;
3347 debugmsg(3, "output_benchmark called", @_);
3348 # This just outputs the benchmark info
3350 my $bench = timediff($bend, $bstart);
3352 my $time;
3353 if($nohires) {
3354 # the times will be seconds
3355 $time = $hrend - $hrstart;
3356 } else {
3357 eval q{$time = tv_interval($hrstart, $hrend)};
3358 $time = sprintf("%.2f", $time);
3361 unless($opt_bench || $conf{extended_benchmarks}) {
3362 # convert $time to something more readable
3363 $time =~ s/\.(\d+)$//;
3364 my $decimal = $1;
3365 my @tparts;
3366 my $tmp;
3367 if(($tmp = int($time / 604800)) >= 1) {
3368 push(@tparts, "$tmp week" . ($tmp != 1 && 's'));
3369 $time %= 604800;
3371 if(($tmp = int($time / 86400)) >= 1) {
3372 push(@tparts, "$tmp day" . ($tmp != 1 && 's'));
3373 $time %= 86400;
3375 if(($tmp = int($time / 3600)) >= 1) {
3376 push(@tparts, "$tmp hour" . ($tmp != 1 && 's'));
3377 $time %= 3600;
3379 if(($tmp = int($time / 60)) >= 1) {
3380 push(@tparts, "$tmp minute" . ($tmp != 1 && 's'));
3381 $time %= 60;
3383 $time ||= '0';
3384 $decimal ||= '00';
3385 $time .= ".$decimal";
3386 push(@tparts, "$time second" . ($time != 1 && 's'));
3387 $time = join(", ", @tparts);
3390 if($opt_bench || $conf{extended_benchmarks}) {
3391 print STDERR "$string\[ $time second" . ($time != 1 && 's')
3392 . " ] [" . timestr($bench) . " ]$string2";
3393 } else {
3394 print STDERR "$string$time$string2";
3398 sub format_output {
3399 my($sth, $format, $num_rows, $sqlstr, $op, $op_text) = @_;
3400 debugmsg(3, "format_output called", @_);
3401 # Formats the output according to the query terminator. If it was a ';' or
3402 # a '/' then a normal table is output. If it was a '\g' then all the columns # and rows are output put line by line.
3403 # input: $sth $format
3404 # sth is the statement handler
3405 # format can be either 'table', 'list', or 'list_aligned'
3406 # output: returns 0 on error, ($success_code, $rows_affected) on success
3407 # $success_code = ('select', 'affected');
3409 debugmsg(3,"type: [" . Dumper($sth->{TYPE}) . "]");
3411 # Is this query a select?
3412 my $isselect = 1 if $sqlstr =~ /^\s*select/i;
3414 if($format eq 'table') {
3415 my $count = 0;
3416 my $res = [];
3417 my $overflow = 0;
3418 while(my @res = $sth->fetchrow_array()) {
3419 push(@$res, \@res);
3420 $count++;
3421 if($count > 1000) {
3422 debugmsg(1,"overflow in table output, switching to serial mode");
3423 $overflow = 1;
3424 last;
3426 debugmsg(1,"num_rows hit on fetch") if $num_rows && $count >= $num_rows;
3427 last if $num_rows && $count >= $num_rows;
3428 return(0) if $sigintcaught; #pseudo sig handle
3431 # If we didn't get any rows back, then the query was probably an insert or
3432 # update, so we call format_affected
3433 if(@$res <= 0 && !$isselect) {
3434 return($sth->rows(), 'affected');
3437 return(0) if $sigintcaught; #pseudo sig handle
3439 # First go through all the return data to determine column widths
3440 my @widths;
3441 for( my $i = 0; $i < @{$res}; $i++ ) {
3442 for( my $j = 0; $j < @{$res->[$i]}; $j++ ) {
3443 if(length($res->[$i]->[$j]) > $widths[$j]) {
3444 $widths[$j] = length($res->[$i]->[$j]);
3447 return(0) if $sigintcaught; #pseudo sig handle
3448 debugmsg(1,"num_rows hit on calc") if $num_rows && $i >= $num_rows-1;
3449 last if $num_rows && $i >= $num_rows-1;
3452 return(0) if $sigintcaught; #pseudo sig handle
3454 my $fields = $sth->{NAME};
3455 my $types = $sth->{TYPE};
3456 my $nullable = $sth->{NULLABLE};
3458 debugmsg(4, "fields: [" . Dumper($fields) . "]");
3459 debugmsg(4, "types: [" . Dumper($types) . "]");
3460 debugmsg(4, "nullable: [" . Dumper($nullable) . "]");
3462 return(0) if $sigintcaught; #pseudo sig handle
3464 # Extend the column widths if the column name is longer than any of the
3465 # data, so that it doesn't truncate the column name
3466 for( my $i = 0; $i < @$fields; $i++ ) {
3467 if(length($fields->[$i]) > $widths[$i]) {
3468 debugmsg(3, "Extending $fields->[$i] for name width");
3469 $widths[$i] = length($fields->[$i]);
3471 return(0) if $sigintcaught; #pseudo sig handle
3474 return(0) if $sigintcaught; #pseudo sig handle
3476 # Extend the column widths if the column is NULLABLE so that we'll
3477 # have room for 'NULL'
3478 for( my $i = 0; $i < @$nullable; $i++ ) {
3479 if($nullable->[$i] && $widths[$i] < 4) {
3480 debugmsg(3, "Extending $fields->[$i] for null");
3481 $widths[$i] = 4;
3483 return(0) if $sigintcaught; #pseudo sig handle
3486 return(0) if $sigintcaught; #pseudo sig handle
3488 my $sumwidths;
3489 foreach(@widths) {
3490 $sumwidths += $_;
3493 return(0) if $sigintcaught; #pseudo sig handle
3495 debugmsg(2,"fields: [" . join("|", @$fields) . "] sumwidths: [$sumwidths] widths: [" . join("|", @widths) . "]\n");
3497 return(0) if $sigintcaught; #pseudo sig handle
3499 # now do the actual outputting, starting with the header
3500 my $rows_selected = 0;
3501 if(@$res) {
3502 if(!$opt_batch) {
3503 print FOUT "\r\e[K" if $op eq '<';
3504 print FOUT "\n";
3505 for( my $i = 0; $i < @$fields; $i++ ) {
3506 if($opt_batch) {
3507 print FOUT "\t" if $i > 0;
3508 print FOUT sprintf("%s", $fields->[$i]);
3510 else
3512 print FOUT " " if $i > 0;
3513 if($types->[$i] == 3 || $types->[$i] == 8) {
3514 print FOUT sprintf("%$widths[$i]s", $fields->[$i]);
3515 } else {
3516 print FOUT sprintf("%-$widths[$i]s", $fields->[$i]);
3520 print FOUT "\n";
3522 for( my $i = 0; $i < @$fields; $i++ ) {
3523 print FOUT " " if $i > 0;
3524 print FOUT '-' x $widths[$i];
3526 print FOUT "\n";
3529 return(0) if $sigintcaught; #pseudo sig handle
3531 # now print the actual data rows
3532 my $count = 0;
3533 for( my $j = 0; $j < @$res; $j++ ) {
3534 $count = $j;
3535 for( my $i = 0; $i < @$fields; $i++ ) {
3536 print FOUT " " if $i > 0;
3537 my $data = $res->[$j]->[$i];
3538 # Strip out plain ole \r's since SQL*Plus seems to...
3539 $data =~ s/\r//g;
3540 $data = 'NULL' unless defined $data;
3541 if($types->[$i] == 3 || $types->[$i] == 8) {
3542 print FOUT sprintf("%$widths[$i]s", $data);
3543 } else {
3544 print FOUT sprintf("%-$widths[$i]s", $data);
3547 print FOUT "\n";
3549 $rows_selected++;
3550 debugmsg(2,"num_rows hit on output") if $num_rows && $j >= $num_rows-1;
3551 last if $num_rows && $j >= $num_rows-1;
3552 return(0) if $sigintcaught; #pseudo sig handle
3555 if($overflow) {
3556 # output the rest of the data from the statement handler
3557 while(my $res = $sth->fetch()) {
3558 $count++;
3559 for( my $i = 0; $i < @$fields; $i++ ) {
3560 print FOUT " " if $i > 0;
3561 my $data = substr($res->[$i],0,$widths[$i]);
3562 # Strip out plain ole \r's since SQL*Plus seems to...
3563 $data =~ s/\r//g;
3564 $data = 'NULL' unless defined $data;
3565 if($types->[$i] == 3 || $types->[$i] == 8) {
3566 print FOUT sprintf("%$widths[$i]s", $data);
3567 } else {
3568 print FOUT sprintf("%-$widths[$i]s", $data);
3571 print FOUT "\n";
3573 $rows_selected++;
3574 debugmsg(2,"num_rows hit on output")
3575 if $num_rows && $count >= $num_rows-1;
3576 last if $num_rows && $count >= $num_rows-1;
3577 return(0) if $sigintcaught; #pseudo sig handle
3582 return($rows_selected, 'selected');
3584 } elsif($format eq 'list' || $format eq 'quiet-list' ) {
3585 # output in a nice list format, which is where we print each row in turn,
3586 # with each column on it's own line
3587 # quiet-list doesn't display *** Row...
3588 my $quiet = ($format eq 'quiet-list') ? 1 : 0;
3589 my $fields = $sth->{NAME};
3591 print "\r\e[K" if $op eq '<';
3592 print FOUT "\n";
3594 my $count = 0;
3595 while(my $res = $sth->fetch()) {
3596 print FOUT "\n**** Row: " . ($count+1) . "\n" unless ($quiet);
3597 for( my $i = 0; $i < @$fields; $i++ ) {
3598 my $data = $res->[$i];
3599 $data = 'NULL' unless defined $data;
3600 if ($quiet) {
3601 print FOUT $data . "\n";
3602 }else{
3603 print FOUT $fields->[$i] . ": " . $data . "\n";
3606 $count++;
3607 last if $num_rows && $count >= $num_rows;
3608 return(0) if $sigintcaught; #pseudo sig handle
3611 return(0) if $sigintcaught; #pseudo sig handle
3613 # If we didn't get any rows back, then the query was probably an insert or
3614 # update, so we call format_affected
3615 if($count <= 0 && !$isselect) {
3616 return($sth->rows(), 'affected');
3619 return($count, 'selected');
3621 } elsif($format eq 'list_aligned') {
3622 # output in a nice list format, which is where we print each row in turn,
3623 # with each column on it's own line. The column names are aligned in this
3624 # one (so that the data all starts on the same column)
3626 my $fields = $sth->{NAME};
3628 print "\r\e[K" if $op eq '<';
3629 print FOUT "\n";
3631 my $maxwidth = 0;
3632 for( my $i = 0; $i < @$fields; $i++ ) {
3633 my $len = length($fields->[$i]) + 1; # +1 for the colon
3634 $maxwidth = $len if $len >= $maxwidth;
3637 return(0) if $sigintcaught; #pseudo sig handle
3639 my $count = 0;
3640 while(my $res = $sth->fetch()) {
3641 print FOUT "\n**** Row: " . ($count+1) . "\n";
3642 for( my $i = 0; $i < @$fields; $i++ ) {
3643 my $data = $res->[$i];
3644 $data = 'NULL' unless defined $data;
3645 print FOUT sprintf("%-" . $maxwidth . "s", $fields->[$i] . ":");
3646 print FOUT " " . $data . "\n";
3648 $count++;
3649 last if $num_rows && $count >= $num_rows;
3650 return(0) if $sigintcaught; #pseudo sig handle
3653 return(0) if $sigintcaught; #pseudo sig handle
3655 # If we didn't get any rows back, then the query was probably an insert or
3656 # update, so we call format_affected
3657 if($count <= 0 && !$isselect) {
3658 return($sth->rows(), 'affected');
3661 return($count, 'selected');
3663 } elsif($format eq 'single_output') {
3664 # Outputs a single return column/row without any labeling
3666 print FOUT "\n";
3668 my $res = $sth->fetchrow_array();
3669 print FOUT "$res\n";
3671 my $count = ($res ? 1 : 0);
3673 return(0) if $sigintcaught; #pseudo sig handle
3675 return($count, 'selected');
3677 } elsif($format eq 'csv' || $format eq 'csv_no_header') {
3678 # output in a comma seperated values format. fields with a ',' are quoted
3679 # with '"' quotes, and rows are seperated by '\n' newlines
3681 print "\r\e[K" if $op eq '<';
3682 print FOUT "\n";
3684 # check that Text::CSV_XS was included ok, if not output an error
3685 if($notextcsv) {
3686 soft_err("You must install Text::CSV_XS from CPAN to use this feature");
3687 return(0);
3688 } else {
3689 my $fields = $sth->{NAME};
3691 if($format eq 'csv') {
3692 # Print the column headers
3693 for(my $i = 0; $i < @$fields; $i++) {
3694 print FOUT "," if $i > 0;
3695 print FOUT $fields->[$i];
3697 print FOUT "\n";
3700 my $count = 0;
3701 while(my $res = $sth->fetch()) {
3702 $count++;
3704 $csv->combine(@$res);
3705 print FOUT $csv->string() . "\n";
3707 last if $num_rows && $count >= $num_rows;
3708 return(0) if $sigintcaught; #pseudo sig handle
3711 return(0) if $sigintcaught; #pseudo sig handle
3713 # If we didn't get any rows back, then the query was probably an insert or
3714 # update, so we call format_affected
3715 if($count <= 0 && !$isselect) {
3716 return($sth->rows(), 'affected');
3719 return($count, 'selected');
3721 } elsif($format eq 'sql') {
3722 # Produce SQL insert statements.
3723 print "\r" if $op eq '<';
3724 print FOUT "\n";
3726 my $cols = lc join(', ', @{$sth->{NAME}});
3727 my @types = map { scalar $dbh->type_info($_)->{TYPE_NAME} } @{ $sth->{TYPE} };
3728 my %warned_unknown_type;
3730 my $count = 0;
3731 while(my $res = $sth->fetch()) {
3732 $count++;
3733 die if @$res != @types;
3734 print FOUT "insert into TABLE ($cols) values (";
3735 foreach (0 .. $#$res) {
3736 my $t = $types[$_];
3737 my $v = $res->[$_];
3738 if (not defined $v) {
3739 print FOUT 'null';
3740 } else {
3741 if ($t eq 'DOUBLE' or $t eq 'DOUBLE PRECISION' or
3742 $t eq 'NUMBER' or $t eq 'DECIMAL') {
3743 die "bad number: $v" if $v !~ /\d/;
3744 print FOUT $v;
3745 } elsif ($t eq 'VARCHAR2' or $t eq 'CHAR' or $t eq 'CLOB') {
3746 $v =~ s/['']/''/g;
3747 print FOUT "'$v'";
3748 } elsif ($t eq 'DATE') {
3749 print FOUT "'$v'";
3750 } else {
3751 warn "don't know how to handle SQL type $t"
3752 unless $warned_unknown_type{$t}++;
3753 print FOUT "(unknown type $t: $v)";
3756 print FOUT ', ' unless $_ eq $#$res;
3758 print FOUT ");\n";
3759 last if $num_rows && $count >= $num_rows;
3760 return(0) if $sigintcaught; #pseudo sig handle
3762 return(0) if $sigintcaught; #pseudo sig handle
3764 # If we didn't get any rows back, then the query was probably an insert or
3765 # update, so we call format_affected
3766 if($count <= 0 && !$isselect) {
3767 return($sth->rows(), 'affected');
3769 return($count, 'selected');
3770 } else {
3771 die("Invalid format: $format");
3775 sub format_affected {
3776 my($rows_affected, $success_code) = @_;
3777 debugmsg(3, "format_affected called", @_);
3778 # This just outputs the given number
3780 return("$rows_affected row" . ($rows_affected == 1 ? '' : 's')
3781 ." $success_code");
3784 sub statusline {
3785 my($num, $max) = @_;
3786 debugmsg(3, "statusline called", @_);
3787 my $linewidth;
3788 eval q{
3789 use Term::ReadKey;
3790 (\$linewidth) = GetTerminalSize();
3792 if($@) {
3793 $linewidth = 80;
3795 my $numwidth = length($num);
3796 my $maxwidth = length($max);
3797 my $width = $linewidth - $numwidth - $maxwidth - 9;
3799 my $fillnum = (($num / $max) * $width);
3800 my $spacenum = ((($max - $num) / $max) * $width);
3802 if($fillnum =~ /\./) {
3803 $fillnum = int($fillnum) + 1;
3806 if($spacenum =~ /\./) {
3807 $spacenum = int($spacenum);
3810 my $fill = ('*' x $fillnum);
3811 my $space = ('-' x $spacenum);
3812 my $pcnt = sprintf("%.0d", ($num / $max * 100));
3814 return(sprintf("%-" . $linewidth . "s", "$num/$max [" . $fill . $space . "] $pcnt\%") . "\r");
3817 sub statusprint {
3818 my($string) = @_;
3820 return("\r\e[K$string\n");
3823 sub ping {
3824 debugmsg(3, "ping called", @_);
3825 if(!$dbh) {
3826 return(0);
3827 } else {
3828 # install alarm signal handle
3829 $SIG{ALRM} = \&sighandle;
3830 debugmsg(2, "Setting alarm for ping ($conf{connection_timeout} seconds)");
3831 alarm($conf{connection_timeout});
3833 debugmsg(2, "Pinging...");
3834 if($dbh->ping()) {
3835 debugmsg(2, "Ping successfull");
3836 alarm(0); # cancel alarm
3837 return(1);
3838 } else {
3839 debugmsg(2, "Ping failed");
3840 alarm(0); # cancel alarm
3841 db_reconnect();
3842 return(0);
3845 alarm(0); # cancel alarm
3848 sub query_err {
3849 my($query_type, $msg, $query) = @_;
3850 debugmsg(3, "query_err called", @_);
3851 # outputs a standard query error. does not exit
3852 # input: $query_type, $msg, $query
3854 chomp($query_type);
3855 chomp($msg);
3856 chomp($query);
3858 print STDERR "\n";
3859 print STDERR "$msg\n";
3860 print STDERR "Query: $query\n" if $query && $conf{sql_query_in_error};
3861 print STDERR "\n";
3864 sub lerr {
3865 my($msg) = @_;
3866 debugmsg(3, "err called", @_);
3867 # outputs an error message and exits
3869 print "Error: $msg\n";
3870 quit(1);
3873 sub soft_err {
3874 my($msg) = @_;
3875 debugmsg(3, "soft_err called", @_);
3876 # outputs a error, but doesn't exit
3878 print "\nError: $msg\n\n";
3881 sub wrn {
3882 my($msg) = @_;
3883 debugmsg(3, "wrn called", @_);
3884 # outputs a warning
3886 print STDERR "Warning: $msg\n";
3889 sub quit {
3890 my($exitcode, $force_quit, $msg) = @_;
3891 debugmsg(3, "quit called", @_);
3892 # just quits
3893 $exitcode ||= 0;
3894 $force_quit ||= 0; # Set this to 1 to try a smoother force quit
3895 $msg ||= '';
3897 setup_sigs();
3899 print "$msg" if $msg && $msg != "";
3900 $quitting = 1;
3902 if($force_quit) {
3903 exit($exitcode);
3906 commit_on_exit();
3908 # disconnect the database
3909 debugmsg(1, "disconnecting from database");
3910 if (defined $dbh) {
3911 $dbh->disconnect()
3912 or warn "Disconnect failed: $DBI::errstr\n";
3915 debugmsg(1, "exiting with exitcode: [$exitcode]");
3916 exit($exitcode);
3919 sub commit_on_exit {
3920 debugmsg(3, "commit_on_exit called", @_);
3922 # Commit... or not
3923 if($conf{commit_on_exit} && defined $dbh && !$dbh->{AutoCommit}) {
3924 # do nothing, oracle commits on disconnect
3925 } elsif(defined $dbh && !$dbh->{AutoCommit}) {
3926 print "Rolling back any outstanding transaction...\n";
3927 $dbh->rollback()
3928 or warn "Rollback failed: $DBI::errstr\n";
3932 sub debugmsg {
3933 my($debuglevel, @msgs) = @_;
3934 if($opt_debug >= $debuglevel ) {
3935 my @time = localtime();
3936 my $time = sprintf("%.4i-%.2i-%.2i %.2i:%.2i:%.2i", $time[5] + 1900,
3937 $time[4] + 1, $time[3], $time[2], $time[1], $time[0]);
3938 print STDERR "$time $debuglevel [" . join("] [", @msgs) . "]\n";
3942 sub usage {
3943 my($exit) = @_;
3944 debugmsg(3, "usage called", @_);
3946 $exit ||= 0;
3948 print <<_EOM_;
3949 Usage: yasql [options] [logon] [AS {SYSDBA|SYSOPER}] [@<file>[.ext]
3950 [<param1> <param2> ...]]
3951 Logon: <username>[/<password>][@<connect_string>] | /
3952 Options:
3953 -d, --debug=LEVEL Turn debugging on to LEVEL
3954 -H, --host=HOST Host to connect to
3955 -p, --port=PORT Host port to connect to
3956 -s, --sid=SID Oracle SID to connect to
3957 -h, -?, --help This help information
3958 -A, --nocomp Turn off building the auto-completion list
3959 -b, --bench, --benchmark Display extra benchmarking info
3960 -v, --version Print version and exit
3961 -B, --batch Batch mode (no headers, etc.)
3963 See the man pages for more help.
3964 _EOM_
3966 exit($exit);
3969 sub help {
3970 debugmsg(3, "help called", @_);
3971 # This just outputs online help
3973 my $help = <<_EOM_;
3975 Commands:
3976 help This screen
3977 quit, exit, \\q Exit the program.
3978 !<cmd>, host <cmd> Sends the command directly to a shell.
3979 \\A Regenerate the auto-completion list.
3980 connect [logon] [AS {SYSDBA|SYSOPER}]
3981 Open new connection.
3982 login = <username>[/<password>][@<connect_string>] | /
3983 reconnect, \\r Reconnect to the database
3984 desc[ribe] <object> Describe table, view, index, sequence, primary key,
3985 foreign key, constraint or trigger
3986 object = [<schema>.]<object>[\@dblink]
3987 show [all] <string> { like <name> }
3988 Shows [all] objects of a certain type
3989 string = tables, views, objects, sequences, clusters,
3990 dimensions, functions, procedures, packages,
3991 indexes, indextypes, libraries, snapshots,
3992 materialized views, synonyms, triggers,
3993 constraints
3994 name : use % for wildcard
3995 show <string> on|for <object>
3996 Shows properties for a particular object
3997 string = indexes, constraints, keys, checks, triggers,
3998 query, deps, ddl
3999 show processes Shows logged in users
4000 show locks Shows locks
4001 show [all] waits Shows [all] waits
4002 show plan Shows the last EXPLAIN PLAN ran
4003 show errors Shows errors from PL/SQL object creation
4004 l[ist], \\l, \\p List the contents of the current buffer
4005 cl[ear] [buffer], \\c
4006 Clear the current buffer
4007 ed[it] [filename], \\e [filename]
4008 Will open a text editor as defined by the EDITOR
4009 environment variable. If a file is given as the
4010 argument, then the editor will be opened with that
4011 file. If the given file does not exist then it will be
4012 created. In both cases the file will not be deleted,
4013 and the current buffer will be overwritten by the
4014 contents of the file. If no file is given, then the
4015 editor will be opened with a temporary file, which will
4016 contain the current contents of the buffer, or the last
4017 execute query if the buffer is empty. After the editor
4018 quits, the file will be read into the buffer. The
4019 contents will be parsed and executed just as if you had
4020 typed them all in by hand. You can have multiple
4021 commands and/or queries. If the last command is not
4022 terminated them you will be able to add furthur lines
4023 or input a terminator to execute the query.
4024 \@scriptname Execute all the commands in <filename> as if they were
4025 typed in directly. All CLI commands and queries are
4026 supported. yasql will quit after running all
4027 commands in the script.
4028 debug [num] Toggle debuggin on/off or if <num> is specified, then
4029 set debugging to that level
4030 autocommit Toggle AutoCommit on/off
4031 set <string> Set options
4032 string = [
4033 [long_read_len <size>]
4034 || [ fast_describe [on|off]]
4035 || [ serverout{put} [on|off] {size <size>} ]
4037 let <search string> Display all configurations
4039 Queries:
4040 All other input is treated as a query, and is sent straight to the database.
4042 All queries must be terminated by one of the following characters:
4043 ; - Returns data in table form
4044 / - Returns data in table form
4045 \\g - Returns data in non-aligned list form
4046 \\G - Returns data in aligned list form
4047 \\s - Returns data in CSV form. The first line is the column names
4048 \\S - Returns data in CSV form, but no column names
4049 \\i - Returns data in sql select commands form
4051 You may re-run the last query by typing the terminator by itself.
4053 Example:
4054 user\@ORCL> select * from table;
4055 user\@ORCL> \\g
4057 Return limit:
4058 You may add a number after the terminator, which will cause only the
4059 first <num> rows to be returned. e.g. 'select * from table;10' will run
4060 the query and return the first 10 rows in table format. This will also work
4061 if you just type the terminator to rerun the last query.
4063 Examples:
4064 The following will run the query, then run it again with different settings:
4065 user\@ORCL> select * from table;10
4066 user\@ORCL> \G50
4068 Redirection:
4069 You can add a shell like redirection operator after a query to pipe the output
4070 to or from a file.
4072 Output:
4073 You can use either '>' or '>>' to output to a file. '>' will overwrite the
4074 file and '>>' will append to the end of the file. The file will be created
4075 if it does not exist.
4077 Examples:
4078 user\@ORCL> select * from table; > table.dump
4079 user\@ORCL> select * from table\S > table.csv
4081 Input:
4082 You can use '<' to grab data from a CSV file. The file must be formatted
4083 with comma delimiters, quoted special fields, and rows seperated by
4084 newlines. When you use this operator with a query, the query will be ran
4085 for every line in the file. Put either '?' or ':n' (n being a number)
4086 placeholders where you want the data from the CSV file to be interpolated.
4087 The number of placeholders must match the number of columns in the CSV file.
4088 Each query is run as if you had typed it in, so the AutoCommit setting
4089 applies the same. If there is an error then the process will stop, but no
4090 rollback or anything will be done.
4092 Examples:
4093 user\@ORCL> insert into table1 values (?,?,?); < table1.csv
4094 user\@ORCL> update table2 set col1 = :1, col3 = :3, col2 = :2; < table2.csv
4096 Piping
4097 You can pipe the output from a query to the STDIN of any program you wish.
4099 Examples:
4100 user\@ORCL> select * from table; | less
4101 user\@ORCL> select * from table; | sort -n
4103 Please see 'man yasql' or 'perldoc yasql' for more help
4104 _EOM_
4106 my $ret = open(PAGER, "|$conf{pager}");
4107 if($ret) {
4108 print PAGER $help;
4109 close(PAGER);
4110 } else {
4111 print $help;
4115 __END__
4117 =head1 NAME
4119 yasql - Yet Another SQL*Plus replacement
4121 =head1 SYNOPSIS
4123 B<yasql> [options] [logon] [@<file>[.ext] [<param1> <param2>]
4125 =over 4
4127 =item logon
4129 <I<username>>[/<I<password>>][@<I<connect_string>>] | /
4131 =item options
4133 =over 4
4135 =item -d I<debuglevel>, --debug=I<debuglevel>
4137 Turn debuggin on to I<debuglevel> level. Valid levels: 1,2,3,4
4139 =item -H I<hostaddress>, --host=I<hostaddress>
4141 Host to connect to
4143 =item -p I<hostport>, --port=I<hostport>
4145 Host port to connect to
4147 =item -s I<SID>, --sid=I<SID>
4149 Oracle SID to connect to
4151 =item -h, -?, --help
4153 Output usage information and quit.
4155 =item -A, --nocomp
4157 Turn off the generation of the auto-completion list at startup. Use This if
4158 it takes too long to generate the list with a large database.
4160 =item -b, --bench, --benchmark
4162 Turn on extended benchmark info, which includes times and CPU usages for both
4163 queries and formatting.
4165 =item -v, --version
4167 Print version and exit
4169 =back
4171 =item Examples
4173 =over 4
4175 =item Connect to local database
4177 =over 4
4179 =item yasql
4181 =item yasql user
4183 =item yasql user/password
4185 =item yasql user@LOCAL
4187 =item yasql user/password@LOCAL
4189 =item yasql -h localhost
4191 =item yasql -h localhost -p 1521
4193 =item yasql -h localhost -p 1521 -s ORCL
4195 =back
4197 =item Connect to remote host
4199 =over 4
4201 =item yasql user@REMOTE
4203 =item yasql user/password@REMOTE
4205 =item yasql -h remote.domain.com
4207 =item yasql -h remote.domain.com -p 1512
4209 =item yasql -h remote.domain.com -p 1512 -s ORCL
4211 =back
4213 =back
4215 =back
4217 If no connect_string or a hostaddress is given, then will attempt to connect to
4218 the local default database.
4220 =head1 DESCRIPTION
4222 YASQL is an open source Oracle command line interface. YASQL features a much
4223 kinder alternative to SQL*Plus's user interface. This is meant to be a
4224 complete replacement for SQL*Plus when dealing with ad hoc queries and general
4225 database interfacing. It's main features are:
4227 =over 4
4229 =item Full ReadLine support
4231 Allows the same command line style editing as other ReadLine enabled programs
4232 such as BASH and the Perl Debugger. You can edit the command line as well as
4233 browse your command history. The command
4234 history is saved in your home directory in a file called .yasql_history. You
4235 can also use tab completion on all table and column names.
4237 =item Alternate output methods
4239 A different style of output suited to each type of need. There are currently
4240 table, list and CSV output styles. Table style outputs in the same manner as
4241 SQL*Plus, except the column widths are set based on the width of the data in
4242 the column, and not the column length defined in the table schema. List outputs
4243 each row on it's own line, column after column for easier viewing of wide return
4244 results. CSV outputs the data in Comma Seperated Values format, for easy
4245 import into many other database/spreadsheet programs.
4247 =item Output of query results
4249 You can easily redirect the output of any query to an external file
4251 =item Data Input and Binding
4253 YASQL allows you to bind data in an external CSV file to any query, using
4254 standard DBI placeholders. This is the ultimate flexibility when inserting or
4255 updating data in the database.
4257 =item Command pipes
4259 You can easily pipe the output of any query to an external program.
4261 =item Tab completion
4263 All tables, columns, and other misc objects can be completed using tab, much
4264 like you can with bash.
4266 =item Easy top rownum listings
4268 You can easily put a number after a terminator, which will only output those
4269 number of lines. No more typing "where rownum < 10" after every query. Now
4270 you can type 'select * from table;10' instead.
4272 =item Enhanced Data Dictionary commands
4274 Special commands like 'show tables', 'desc <table>', 'show indexes on <table>',
4275 'desc <sequence>', and many many more so that you can easily see your schema.
4277 =item Query editing
4279 You can open and edit queries in your favorite text editor.
4281 =item Query chaining
4283 You can put an abitrary number of queries on the same line, and each will be
4284 executed in turn.
4286 =item Basic scripting
4288 You can put basic SQL queries in a script and execute them from YASQL.
4290 =item Config file
4292 You can create a config file of options so that you don't have to set them
4293 everytime you run it.
4295 =item Future extensibility
4297 We, the community, can modify and add to this whatever we want, we can't do that
4298 with SQL*Plus.
4300 =back
4302 =head1 REQUIREMENTS
4304 =over 4
4306 =item Perl 5
4308 This was developed with Perl 5.6, but is known to work on 5.005_03 and above.
4309 Any earlier version of Perl 5 may or may not work. Perl 4 will definately not
4310 work.
4312 =item Unix environment
4314 YASQL was developed under GNU/Linux, and aimed at as many Unix installations as
4315 possible. Known to be compatible with GNU/Linux, AIX and Sun Solaris.
4316 Please send me an email (qzy@users.sourceforge.net) if it works for other platforms.
4317 I'd be especially interested if it worked on Win32.
4319 =item Oracle Server
4321 It has been tested and developed for Oracle8 and Oracle8i. There is atleast
4322 one issue with Oracle7 that I know of (see ISSUES below) and I have not tested
4323 it with Oracle9i yet.
4325 =item Oracle client libraries
4327 The Oracle client libraries must be installed for DBD::Oracle. Of course you
4328 can't install DBD::Oracle without them...
4330 =item DBD::Oracle
4332 DBD::Oracle must be installed since this uses DBI for database connections.
4334 =item ORACLE_HOME
4336 The ORACLE_HOME environment variable must be set if you use a connection
4337 descriptor to connect so that YASQL can translate the descriptor into
4338 usefull connection information to make the actual connection.
4340 =item ORACLE_SID
4342 The ORACLE_SID environment variable must be set unless you specify one with the
4343 -s option (see options above).
4345 =item Term::ReadLine
4347 Term::ReadLine must be installed (it is with most Perl installations), but more
4348 importantly, installing Term::ReadLine::Gnu from CPAN will greatly enhance the
4349 usability.
4351 =item Time::HiRes
4353 This is used for high resolution benchmarking. It is optional.
4355 =item Text::CSV_XS
4357 This perl module is required if you want to output CSV or input from CSV files.
4358 If you don't plan on using this features, then you don't need to install this
4359 module.
4361 =item Term::ReadKey
4363 This module is used for better input and output control. Right now it isn't
4364 required, but some parts of YASQL will look and function better with this
4365 installed.
4367 =back
4369 =head1 CONFIG
4371 YASQL will look for a config file first in ~/.yasqlrc then
4372 /etc/yasql.conf. The following options are available:
4374 =over 4
4376 =item connection_timeout = <seconds>
4378 Timeout for connection attempts
4380 Default: 20
4382 =item max_connection_attempts = <num>
4384 The amount of times to attempt the connection if the username/password are wrong
4386 Default: 3
4388 =item history_file = <file>
4390 Where to save the history file. Shell metachars will be globbed (expanded)
4392 Default: ~/.yasql_history
4394 =item pager = <file>
4396 Your favorite pager for extended output. (right now only the help command)
4398 Default: /bin/more
4400 =item auto_commit = [0/1]
4402 Autocommit any updates/inserts etc
4404 Default: 0
4406 =item commit_on_exit = [0/1]
4408 Commit any pending transactions on exit. Errors or crashes will still cause
4409 the current transaction to rollback. But with this on a commit will occur
4410 when you explicitly exit.
4412 Default: 0
4414 =item long_trunc_ok = [0/1]
4416 Long truncation OK. If set to 1 then when a row contains a field that is
4417 set to a LONG time, such as BLOB, CLOB, etc will be truncated to long_read_len
4418 length. If 0, then the row will be skipped and not outputted.
4420 Default: 1
4422 =item long_read_len = <num_chars>
4424 Long Read Length. This is the length of characters to truncate to if
4425 long_trunc_ok is on
4427 Default: 80
4429 =item edit_history = [0/1]
4431 Whether or not to put the query edited from the 'edit' command into the
4432 command history.
4434 Default: 1
4436 =item auto_complete = [0/1]
4438 Whether or not to generate the autocompletion list on connection. If connecting
4439 to a large database (in number of tables/columns sense), the generation process
4440 could take a bit. For most databases it shouldn't take long at all though.
4442 Default: 1
4444 =item extended_complete_list = [0/1]
4446 extended complete list will cause the possible matches list to be filled by
4447 basicly any and all objects. With it off the tab list will be restricted to
4448 only tables, columns, and objects owned by the current user.
4450 Default: 0
4452 =item complete_tables = [0/1]
4454 This controls whether or not to add tables to the completion list. This does
4455 nothing if auto_complete is set to 0.
4457 Default: 1
4459 =item complete_columns = [0/1]
4461 This controls whether or not to add columns to the completion list. This does
4462 nothing if auto_complete is set to 0.
4464 Default: 1
4466 =item complete_objects = [0/1]
4468 This controls whether or not to add all other objects to the completion list.
4469 This does nothing if auto_complete is set to 0. (Hint... depending on your
4470 schema this will include tables and columns also, so you could turn the other
4471 two off)
4473 Default: 1
4475 =item extended_benchmarks = [0/1]
4477 Whether or not to include extended benchmarking info after queries. Will
4478 include both execution times and CPU loads for both the query and formatting
4479 parts of the process.
4481 Default: 0
4483 =item prompt
4485 A string to include in the prompt. The prompt will always be suffixed by a
4486 '>' string. Interpolated variables:
4487 %H = connected host. will be prefixed with a '@'
4488 %U = current user
4490 Default: %U%H
4492 =item column_wildcards = [0/1]
4494 Column wildcards is an extremely experimental feature that is still being
4495 hashed out due to the complex nature of it. This should affect only select
4496 statements and expands any wildcards (*) in the column list. such as
4497 'select col* from table;'.
4499 Default: 0
4501 =item sql_query_in_error = [0/1]
4503 This this on to output the query in the error message.
4505 Default: 0
4507 =item nls_date_format = <string>
4509 Set the preferred NLS_DATE_FORMAT. This effects both date input and output
4510 formats. The default is ISO standard (YYYY-MM-DD HH24:MI:SS', not oracle
4511 default (YYYY-MM-DD).
4513 Default: YYYY-MM-DD HH24:MI:SS
4515 =item fast_describe
4517 Turn on fast describes. These are much faster than the old style of desc
4518 <table>, however non-built in datatypes may not be returned properly. i.e. a
4519 FLOAT will be returned as a NUMBER type. Internally FLOATs really are just
4520 NUMBERs, but this might present problems for you. If so, set this to 0
4522 Default: 1
4524 =back
4526 =head1 ISSUES
4528 =over 4
4530 =item Oracle7
4532 DBD::Oracle for Oracle8 may have issues connecting to an Oracle7 database. The
4533 one problem I have seen is that the use of placeholders in a query will cause
4534 oracle to issue an error "ORA-01008: not all variables bound". This will affect
4535 all of the hard-coded queries that I use such as the ones for the 'desc' and
4536 'show' commands. The queries that you type in on the command line may still
4537 work. The DBD::Oracle README mentions the use of the '-8' option to the
4538 'perl Makefile.PL' command to use the older Oracle7 OCI. This has not been
4539 tested.
4541 =back
4543 =head1 AUTHOR
4545 Originaly written by Nathan Shafer (B<nshafer@ephibian.com>) with support from
4546 Ephibian, Inc. http://www.ephibian.com
4547 Now it is mostly developed and maintained by Balint Kozman
4548 (B<qzy@users.sourceforge.net>). http://www.imind.hu
4550 =head1 THANKS
4552 Thanks to everyone at Ephibian that helped with testing, and a special thanks
4553 to Tom Renfro at Ephibian who did a lot of testing and found quite a few
4554 doozies.
4555 Also a lot of thanks goes to the mates at iMind.dev who keep suffering from
4556 testing new features on them.
4558 The following people have also contributed to help make YASQL what it is:
4559 Allan Peda, Lance Klein, Scott Kister, Mark Dalphin, Matthew Walsh
4561 And always a big thanks to all those who report bugs and problems, especially
4562 on other platforms.
4564 =head1 COPYRIGHT
4566 Copyright (C) 2000-2002 Ephibian, Inc., 2005 iMind.dev.
4569 =head1 LICENSE
4571 This program is free software; you can redistribute it and/or
4572 modify it under the terms of the GNU General Public License
4573 as published by the Free Software Foundation; either version 2
4574 of the License, or (at your option) any later version.
4576 This program is distributed in the hope that it will be useful,
4577 but WITHOUT ANY WARRANTY; without even the implied warranty of
4578 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4579 GNU General Public License for more details.
4581 You should have received a copy of the GNU General Public License
4582 along with this program; if not, write to the Free Software
4583 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
4585 =head1 TODO
4587 =over 4
4589 =item desc a synomym doesn't keep the right schema... I think. Saw in desc parking.customer when logged in as cccrsmgr in 3c db
4591 =item allow history to be saved based on host (as an option)
4593 =item make stifle_history a configurable option
4595 =item a row is printed after "Attempting to cancel query"
4597 =item reading from a script will not change prompt properly (for a script with no terminator)
4599 =item NULL stops printing after table goes into overflow or something
4601 =item extra space in \G... maybe others
4603 =item bug: tag completion doesn't work with caps anymore
4605 =item Add support for /NOLOG
4607 =item allow dblinks in show blah on blah commands
4609 =item show query doesn't work with schemas and db links
4611 =item add save and get buffer commands
4613 =item add R[UN] command (/ equivilent)
4615 =item add support for just 'connect' and prompt for username and password
4617 =item add PASSW[ORD] command for changing password
4619 =item add -s[ilent] command line to suppress all startup output and command prompts
4621 =item add 'start' command for scripting
4623 =item add 'run' synonum for '/'
4625 =item add 'show parameters <filter>' support
4627 =item fix segfaults when cancelling large outputs
4629 =item Add a 'SPOOL' command
4631 =item fix 'set...' commands
4633 =item Add variable bindings, prompting, control structures, etc.
4635 =item be able to describe any kind of object
4637 =item Add 'startup queries' in config file or support glogin.sql and login.sql
4639 =item fix case sensitive object names
4641 =item make win32 compliant
4643 =item add better error messages when the user can't access a data dictionary
4644 table
4646 =item add better error output, with line/col numbers and maybe a pointer.
4648 =item add chained ops, exactly like bash
4650 =item add plugins and hooks for all aspects.
4652 =item Add smarter tables and wrapping in columns. Also add configurable max
4653 column widths and max table width.
4655 =item Add a curses interface option for easy viewing and scrolling, etc. This
4656 will require some research to determine if it's even worth it.
4658 =item Add HTML output option
4660 =back
4662 =head1 CHANGELOG
4664 $Log: yasql,v $
4665 Revision 1.83 2005/05/09 16:57:13 qzy
4666 Fixed the 'DECIMAL' problem with describe command.
4667 Added sql mode with \i (patch by Ed Avis).
4668 Added redirectors (>, >>, |) to describe.
4669 Added 'show user' command.
4670 Added 'show uid' command.
4671 Added new makefile targets: clean, check. (patch by Ed Avis)
4672 Added "and owner = ?" to some show targets (patch by anonymous).
4673 Added command_complete_list feature and config option.
4674 Added disconnect command
4675 Added command completion: select, update, insert, delete, execute, etc.
4676 Added table.column name completion.
4677 Added feature to run tty-less (patch by Michael Kroell).
4678 Added a workaround for SunOS's alarm() bug (patch by Ed Avis).
4679 Fixed some minor issues in parser code.
4681 Revision 1.82 2005/02/18 16:57:13 qzy
4682 Added batch mode (ewl patch).
4683 Allow connections AS SYSDBA, AS SYSOPER and internal (sysdba patch by Derek Whayman).
4684 Added server_output to config options.
4685 Changed script execution to only add script lines to the query buffer (and not to history).
4687 Revision 1.81 2002/03/06 21:55:13 nshafer
4688 Fixed bug with password prompt.
4689 Added 'show plan' for outputting last explain plan results.
4690 Added 'show query' for viewing queries for views and materialized views.
4691 Optimized describes to be as fast as describes in SQL*Plus.
4692 Added new option 'fast_describe' on by default for new describe method.
4693 Added single_output as a formatting option for internal use.
4694 Fixed problem with password, quit, exit, \q getting added to the history list.
4695 Changed history to not add duplicate entries right next to each other.
4696 Added support for basic (non-returning) PL/SQL commands.
4697 Added support for create function, package, package body, prodedure, trigger.
4698 Added 'show errors' command
4699 Added 'conn' shortcut for 'connection'.
4700 Added 'exec[ute]' command.
4701 Added 'set serverout[put] on|off' command to mimic SQL*Plus's.
4702 Added alarms to pings in cases where DB connection is dropped and ping hangs.
4703 Cleaned up error messages.
4704 Renamed config options AutoCommit, CommitOnExit, LongTruncOk, and LongReadLen toauto_commit, commit_on_exit, long_trunc_ok, and long_read_len. Old names are now deprecated.
4705 Changed quote escaping to be '' and "" instead of \' and \".
4706 Added full support for comments: rem[ark], --, and /* */.
4707 Right-justify works for the '8' datatype as well as '3' now.
4708 Re-worked debug output levels.
4709 Optimized query for completion lists a bit.
4710 Added completion-list limiting based on location in some DML statements (select, update, insert).
4711 Fixed up the display of '...' when generating tab completion list. Should work a lot better when hitting tab in the middle of the line.
4712 Added show views, objects, sequences, clusters, dimensions, functions, procedures, packages, indexes, indextypes, libraries, materialized views, snapshots, synonyms, triggers.
4713 Added show all <objects> command.
4714 Added type and owner columns to show commands.
4715 Fixed commit_on_exit logic.
4716 Added ability to use external authentication ('yasql /').
4717 The .sql extension for the scripting and editing commands are now optional.
4718 Fixed up editor execution to hopefully find the editor better.
4719 Added "Command" entry to "show processes".
4720 Added "show waits" and "show all waits" commands.
4721 Re-organized command line usage in anticipation for script parameters.
4722 Removed all uses of 'stty'.
4723 Added processing of STDIN, so redirects and pipes to YASQL work now.
4724 Changed benchmarking to include time for fetching... this should work better with Oracle 7.x, which doesn't seem to execute the query until you try fetching
4725 Updated documentation.
4726 Fixed up alarm() calls.
4727 Fixed setting of NLS_DATE_FORMAT to apply on reconnects.
4728 Broke commands into 2 sets... ones that exectute any time, and ones that execute only when nothing is in the buffer
4729 Fixed printing of text read in from an edit command. It now echoes all of it.
4730 Now ignoring most SET commands so we don't tack them onto queries
4731 Fixed permissions in tarball
4733 Revision 1.80 2001/08/01 18:06:27 nshafer
4734 Fixed bug with delayed $term initialization\e\b
4736 Revision 1.79 2001/08/01 17:52:35 nshafer
4737 Fixed compatibility issues with the data dictionary in Oracle 7. Fixed ordering
4738 of indexes for compound indexes. Fixed display of objects from other schemas
4739 in some data dictionary commands such as 'show indexes on table'. (Thanks Nix)
4740 Fixed matching of declare and end in query string. Will not only match if on
4741 blank line. Fixed matching of '/' terminator in middle of queries. Will now
4742 only match if at end of line (Thanks Wesley Hertlein). Temp file for editing
4743 now appends '.sql' to end of temp file so that editors, like vim, automatically
4744 turn on syntax highlighting. Added searching of environment variable SQLPATH
4745 when looking for scripts. Terminal setup is now after script parsing, so that
4746 it will work when run under cron (Thanks David Zverina).
4748 Revision 1.78 2001/07/05 13:52:56 nshafer
4749 Fixed bug where parens were matching improperly.
4751 Revision 1.77 2001/07/04 02:57:08 nshafer
4752 Fixed bug where terminators wouldn't match if they were the next character
4753 after a quote character.
4755 Revision 1.76 2001/06/28 04:17:53 nshafer
4756 Term::ReadLine::Perl now supported, for what little functionality it does
4757 provide. Fixed segfault when hitting up when history is empty. Fixed bug
4758 when providing script names on command line (Thanks to Dave Zverina.)
4759 Rewrote the query parser to fix a bug, caused by the multiple-queries-on-one-
4760 line feature, that causes terminators, such as ';' and '/' to match when in
4761 quotes. When hitting tab on a line starting with a '@' for scripts, tab will
4762 now complete filenames and not database objects. Fixed DB timeout when
4763 prompting for username and password. Added support for 'DECLARE' keyword,
4764 however this does not mean that variable binding in PL/SQL blocks works yet.
4765 Sped up startup time a bit more (hopefully).
4767 Revision 1.75 2001/06/19 16:02:16 nshafer
4768 Fixed typo in error message for Term::ReadLine::Gnu
4769 Fixed crash when tab hit at username or password prompt
4770 Added -- as a comment type and fixed case where comment in quotes would
4771 match. (Mark Dalphin)
4772 Fixed 'desc' to also describe partitioned tables (Erik)
4774 Revision 1.74 2001/06/18 21:07:55 nshafer
4775 Fixed bug where / would not rerun last query (thanks Scott Kister)
4777 Revision 1.73 2001/05/23 18:35:17 nshafer
4778 Got rid of "Prototype mismatch" errors. Fixed typo in extended benchmarks
4780 Revision 1.72 2001/05/22 16:06:36 nshafer
4781 Fixed bug with error messages not displaying first time, and fixed bug with
4782 tab completion output
4784 Revision 1.71 2001/05/17 21:28:40 nshafer
4785 New CSV output format. Added CSV file input on any query. Added ability to
4786 pipe query results to any program. Added ability for multiple queries on one
4787 line. Changed tab completion generator to run first time you hit tab instead
4788 of on startup, which speeds up database connection. Now using SelfLoader to
4789 speed up loading and minimize memory use. Added a 'show plan for ____' command
4790 for easy display of explain plan output. Query times are now more readable
4791 and will split into weeks, days, hours, minutes, and seconds. Hopefully fixed
4792 some problems with stty and Solaris 2.4. Added support for 'rem' comments in
4793 scripts. Redirection output files are now shell expanded.
4795 Revision 1.70 2001/05/08 17:49:51 nshafer
4796 Fixed all places where a non-alphanumeric object name would break or not
4797 match.
4798 Added code for autoconf style installs.
4800 Revision 1.69 2001/05/07 23:47:47 nshafer
4801 fixed type
4803 Revision 1.68 2001/05/07 22:26:20 nshafer
4804 Fixed tab completion problems when completing objects with a $ in their name.
4805 Added config options complete_tables, complete_columns, and complete_objects,
4806 Added redirection of query output to file. Hopefully sped up exiting.
4807 Updated documentation.
4809 Revision 1.67 2001/05/04 17:35:04 nshafer
4810 YASQL will now suspend properly back to the shell when SIGTSTP is sent, as in
4811 when you hit ctrl-z on most systems. Added NLS_DATE_FORMAT setting in config
4812 file to support alter date views. Defaults to ISO standard. YASQL will now
4813 attempt to change it's process name, such as when viewed in ps or top. This
4814 will not work on all systems, nor is it a complete bullet proof way to hide
4815 your password if you provide it on the command line. But it helps to not
4816 make it so obvious to regular users. Scripts entered on the command line are
4817 now checked to be readable before attempting connection. A failed 'connect
4818 command will no long alter the prompt. Added \p option for printing the
4819 current buffer, ala psql. Large query results (over 1000 rows) are now
4820 handled MUCH better. YASQL will no longer try to hold more than 1000 rows in
4821 memory, which keeps it from sucking memory, and also improves the speed.
4822 When a query does return more than 1000 rows in table mode, those first 1000
4823 will determine the column widths, and all rows after that will get truncated.
4824 AIX has been reported to run YASQL perfectly.
4826 Revision 1.66 2001/03/13 21:34:58 nshafer
4827 There are no longer any references to termcap, so yasql should now work on
4828 termcap-less systems such as Debian Linux and AIX
4830 Revision 1.65 2001/03/12 17:44:31 nshafer
4831 Restoring the terminal is hopefully more robust and better now. YASQL now
4832 tries to use the 'stty' program to dump the settings of the terminal on
4833 startup so that it can restore it back to those settings. It requires that
4834 stty is installed in the path, but that should be the case with most systems.
4835 Also made the output of the query in the error message an option that is off
4836 by default. I had never meant to include that in the final release, but kept
4837 on forgetting to take it out.
4839 Revision 1.64 2001/03/06 16:00:33 nshafer
4840 Fixed bug where desc would match anytime, even in middle of query, which is
4841 bad.
4843 Revision 1.63 2001/03/01 17:30:26 nshafer
4844 Refined the ctrl-c process for not-so-linuxy OS's, namely solaris. Now
4845 stripping out Dos carriage returns since SQL*Plus seems to.
4847 Revision 1.62 2001/02/26 22:39:12 nshafer
4848 Fixed bug where prompt would reset itself when a blank line was entered.
4849 Added script argument on command line (Lance Klein)
4850 Added support for any command line commands in the script (Lance Klein)
4851 The 'desc' and 'show' commands no longer require a terminator (like ;) as long as the whole statement is on one line (Lance Klein)
4852 Added option 'extended_tab_list' for a much bigger, more complete tab listing (Lance Klein)
4853 The edit command is no longer limited to 1 query at a time. You can now put any valid command or query, and as many of them as you want. The parsing rules for the edit command is exactly identical to the script parsing.
4854 cleaned up documentation a bit
4856 Revision 1.61 2001/01/31 19:56:22 nshafer
4857 changed CommitOnExit to be 1 by default, to emulate SQL*Plus behavior, and
4858 at popular request
4860 Revision 1.60 2001/01/29 16:38:17 nshafer
4861 got rid of (tm)
4863 Revision 1.59 2001/01/29 16:28:22 nshafer
4864 Modified docs a little with the new scope of open source now in the mix.
4866 Revision 1.58 2001/01/24 15:27:00 nshafer
4867 cleanup_after_signals is not in the Term::ReadLine::Stub, so it would
4868 output error messages on systems without Term::ReadLine::Gnu. Fixed
4870 Revision 1.57 2001/01/17 23:26:53 nshafer
4871 Added Tom Renfro's column_wildcard expansion code. New conf variable:
4872 column_wildcards. 0 by default until this code is expanded on a bit more.
4874 Revision 1.56 2001/01/17 23:00:25 nshafer
4875 Added CommitOnExit config, 0 by default. Added info output at startup and
4876 when a new connection is initiated about the state of AutoCommit and
4877 CommitOnExit. Also added statement about explicit rollback or commit when
4878 disconnecting. Added warning message to commit_cmd and rollback_cmd if
4879 AutoCommit is on. Now explicitly committing or rolling back on disconnect,
4880 it is no longer left up to the DBI's discretion... except in abnormal
4881 termination.
4883 Revision 1.55 2001/01/11 18:05:12 nshafer
4884 Added trap for regex errors in tab completion (like if you put 'blah[' then
4885 hit tab)
4887 Revision 1.54 2001/01/10 17:07:22 nshafer
4888 added output to those last 2 commands
4890 Revision 1.53 2001/01/10 17:03:58 nshafer
4891 added commit and rollback commands so that you don't have to send them to the
4892 backend
4894 Revision 1.52 2001/01/10 16:00:08 nshafer
4895 fixed bug with prompt where on each call get_prompt would add another '@'.
4896 Thanks Tom
4898 Revision 1.51 2001/01/09 21:16:12 nshafer
4899 dar... fixed another bug where the %H would stay if there was no prompt_host
4901 Revision 1.50 2001/01/09 21:12:13 nshafer
4902 fixed bug with that last update. Now it only interpolates the %H variable
4903 if there is something to interpolate it with
4905 Revision 1.49 2001/01/09 21:09:56 nshafer
4906 changed the %H variable to be prefixed with a @
4908 Revision 1.48 2001/01/09 21:04:36 nshafer
4909 changed 'default' to '' for the prompt's hostname when no connect_string is
4910 used
4912 Revision 1.47 2001/01/09 20:55:11 nshafer
4913 added configurable prompt and changed the default prompt
4915 Revision 1.46 2001/01/09 18:50:50 nshafer
4916 updated todo list
4918 Revision 1.45 2001/01/09 18:32:35 nshafer
4919 Added 'connect <connect_string>' command. I may add the ability to specify
4920 options like on the command line (like '-H blah.com')
4922 Revision 1.44 2001/01/08 22:08:49 nshafer
4923 more documentation changes
4925 Revision 1.43 2001/01/08 20:51:31 nshafer
4926 added some documentation
4928 Revision 1.42 2001/01/08 20:09:35 nshafer
4929 Added debug and autocommit commands
4931 Revision 1.41 2001/01/08 18:12:43 nshafer
4932 added END handler to hopefully clean up the terminal better
4934 Revision 1.40 2001/01/05 23:29:38 nshafer
4935 new name!
4937 Revision 1.39 2001/01/05 18:00:16 nshafer
4938 Added config file options for auto completion generation and extended
4939 benchmark info
4941 Revision 1.38 2001/01/05 16:39:47 nshafer
4942 Fixed error where calling edit a second time would not open the file properly
4943 because of the way glob() works.
4945 Revision 1.37 2001/01/04 23:52:30 nshafer
4946 changed the version string to parse it out of the revision string (duh...)
4947 moved the prompting of username and password so that the check for the
4948 oracle_home variable happens before. Before if you didn't have the environment
4949 variable set then it will prompt you for username and password, then die
4950 with the error, which is annoying
4951 fixed the quit calls so taht they properly erase the quit line from the
4952 history. I had broken this a long time ago when I added the exit status
4953 param to the quit function
4954 Outputting in full table format (';' terminator) with a num_rows number
4955 (like ';100') would still cause the entire result set to be pulled into
4956 memory, which was really slow and could take a lot of memory if the table
4957 was large. Fixed it so that it only pulls in num_rows number of rows when
4958 using the digit option
4960 Revision 1.36 2000/12/22 22:12:18 nshafer
4961 fixed a wrong-quote-type in the debug messages
4963 Revision 1.35 2000/12/22 22:07:06 nshafer
4964 forgot version... you know the drill...
4966 Revision 1.34 2000/12/22 21:57:01 nshafer
4967 Added config file support, queries from the 'edit' command are now entered
4968 into the command history (configurable), cleaned up the SIGINT actions quite
4969 a bit so they should work better now, added LongReadLen and LongTruncOk
4970 options so that LONG columns types won't mess up, added the number after terminator
4971 feature to limit how many rows are returned.
4973 Revision 1.33 2000/12/20 22:56:03 nshafer
4974 version number.... again.... sigh
4976 Revision 1.32 2000/12/20 22:55:32 nshafer
4977 added todo item, now in rpms
4979 Revision 1.31 2000/12/20 17:07:52 nshafer
4980 added the reprompt for username/password on error 1005 null password given
4982 Revision 1.30 2000/12/20 17:04:18 nshafer
4983 Refined the shadow_redisplay stuff. Now I will only use my builtin function
4984 if the terminal type is set to "xterm" because that terminal type has a
4985 broken termcap entry. Also set it to not echo when entering password if
4986 Term::ReadLine::Gnu is not installed
4988 Revision 1.29 2000/12/20 15:47:56 nshafer
4989 trying a new scheme for the shadow_redisplay. Clear to EOL wasn't working
4990 Also fixed a few problems in the documentation
4993 Revision 1.28 2000/12/19 23:55:03 nshafer
4994 I need to stop forgetting the revision number...
4996 Revision 1.27 2000/12/19 23:48:49 nshafer
4997 cleaned up debugging
4999 Revision 1.26 2000/12/19 23:10:18 nshafer
5000 Lotsa new stuff... tab completion of table, column, and object names,
5001 improved signal handling, the edit command now accepts a filename parameter,
5002 new command 'show processes' which shows you info on who's connected,
5003 improved benchmark info, and a lot of other cleanup/tweaks
5005 Revision 1.25 2000/12/13 16:58:26 nshafer
5006 oops forgot documentation again
5008 Revision 1.24 2000/12/13 16:54:42 nshafer
5009 added desc <trigger>
5011 Revision 1.23 2000/12/12 17:52:15 nshafer
5012 updated todo list (oops, forgot)
5014 Revision 1.22 2000/12/12 17:51:39 nshafer
5015 added desc <index>
5017 Revision 1.21 2000/12/12 17:15:28 nshafer
5018 fixed bug when connecting using a host string (-H option)
5019 added a few more types to the 'show' and 'desc' commands
5021 Revision 1.20 2000/12/08 22:13:43 nshafer
5022 many little fixes and tweaks here and there
5024 Revision 1.19 2000/12/06 20:50:03 nshafer
5025 added scripting ability with "@<filename>" command
5026 changed all tabs to spaces!
5028 Revision 1.18 2000/12/06 19:30:38 nshafer
5029 added clear command
5030 refined connection process. if invalid username/password entered then prompt again
5032 Revision 1.17 2000/12/05 22:20:58 nshafer
5033 Tightened up outputs. Doesn't show column names if no rows selected, if
5034 it's not a select, then show number of rows affected
5036 Revision 1.16 2000/12/04 18:04:53 nshafer
5037 *** empty log message ***
5039 Revision 1.15 2000/12/04 18:03:14 nshafer
5040 fixed bug where the -H option was interpreted as -h or help. All command
5041 line options are now case sensitive
5043 Revision 1.14 2000/12/04 17:54:38 nshafer
5044 Added list command (and \l and l)
5046 Revision 1.13 2000/12/04 17:34:18 nshafer
5047 fixed a formatting issue if Time::HiRes isn't installed
5049 Revision 1.12 2000/12/04 17:29:41 nshafer
5050 Added benchmark options to view the extended benchmark info. Now it displays
5051 just the time in a more friendly format. The old style is only active if the
5052 benchmark option is specified.
5053 Cleaned up some formatting issues
5054 Brought the usage and POD documentation up to date
5055 Added some items to the TODO
5057 Revision 1.11 2000/11/30 22:54:38 nshafer
5058 Fixed bug with the edit command where if you were 'inquotes' then you would
5059 stay in quotes even after editing the file
5061 Revision 1.10 2000/11/30 22:01:38 nshafer
5062 Fixed bug where username and password were added to the command history.
5063 Set it so that the quit commands are not added to the command history either.
5064 Added the 'edit' command and modified it's todo list item, as well as added
5065 it to the 'help' command
5067 Revision 1.9 2000/11/29 17:55:35 nshafer
5068 changed version from .21 to 1.0 beta 9. I'll follow the revision numbers now
5070 Revision 1.8 2000/11/29 17:46:31 nshafer
5071 added a few items to the todo list
5073 Revision 1.7 2000/11/29 15:50:56 nshafer
5074 got rid of SID output at startup
5076 Revision 1.6 2000/11/29 15:49:51 nshafer
5077 moved revision info to $revision and added Id output
5079 Revision 1.5 2000/11/29 15:46:41 nshafer
5080 fixed revision number
5082 Revision 1.4 2000/11/29 15:44:23 nshafer
5083 fixed issue where environment variable ORACLE_SID overwrote explicit set
5084 on the command line. now whatever you put on the command line will overwrite
5085 the environment variable
5087 =cut