added synonyms to completion
[yasql.git] / yasql.in
blob87023f0f490a64e425331cf393d432b4d3e660b5
1 #! /usr/bin/env perl
2 # vim: set ts=8 smartindent shiftwidth=2 expandtab ai :
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, '
563 select table_name x from all_tables union
564 select view_name x from all_views union
565 select synonym_name x from all_synonyms
568 if($conf{complete_columns}) {
569 push(@queries, 'select column_name from all_tab_columns');
571 if($conf{complete_objects}) {
572 push(@queries, 'select object_name from all_objects');
575 my $sqlstr = join(' union ', @queries);
576 debugmsg(3, "query: [$sqlstr]");
578 my $sth = $dbh->prepare($sqlstr)
579 or query_err('prepare', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
580 $sth->execute()
581 or query_err('execute', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
582 while(my $res = $sth->fetchrow_array()) {
583 push(@completion_list, $res);
585 } else {
586 my @queries;
587 if($conf{complete_tables}) {
588 push(@queries, "
589 select 'table-' || table_name x from user_tables union
590 select 'table-' || view_name x from user_views union
591 select 'table-' || synonym_name x from user_synonyms
594 if($conf{complete_columns}) {
595 push(@queries, "select 'column-' || column_name from user_tab_columns");
597 if($conf{complete_objects}) {
598 push(@queries, "select 'object-' || object_name from user_objects");
601 my $sqlstr = join(' union ', @queries);
602 debugmsg(3, "query: [$sqlstr]");
604 my $sth = $dbh->prepare($sqlstr)
605 or query_err('prepare', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
606 $sth->execute()
607 or query_err('execute', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
608 while(my $res = $sth->fetchrow_array()) {
609 push(@completion_list, $res);
613 if ($conf{command_complete_list}) {
614 push(@completion_list, qw{
615 command-create command-select command-insert command-update
616 command-delete from command-from command-execute command-show
617 command-describe command-drop
619 push(@completion_list, qw{
620 show-objects show-tables show-indexes show-sequences show-views
621 show-functions show-constraints show-keys show-checks show-triggers
622 show-query show-dimensions show-clusters show-procedures show-packages
623 show-indextypes show-libraries show-materialized views show-snapshots
624 show-synonyms show-waits show-processes show-errors show-user show-users
625 show-uid show-plan show-database links show-dblinksshow-recyclebin
626 show-dependencies
630 if ($current_table_name) {
632 my @queries;
633 push(@queries, "select 'current_column-$current_table_name.' || column_name from user_tab_columns where table_name=\'".uc($current_table_name)."\'");
635 my $sqlstr = join(' union ', @queries);
636 debugmsg(3, "query: [$sqlstr]");
638 my $sth = $dbh->prepare($sqlstr)
639 or query_err('prepare', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
640 $sth->execute()
641 or query_err('execute', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
642 while(my $res = $sth->fetchrow_array()) {
643 push(@completion_list, $res);
647 setup_sigs();
649 if($inline_print) {
650 print "\r";
651 print "\e[K";
652 $| = 0;
653 $term->forced_update_display();
657 sub complete_entry_function {
658 my($word, $state) = @_;
659 debugmsg(3, "complete_entry_function called", @_);
660 # This is called by Term::ReadLine::Gnu when a list of matches needs to
661 # be generated. It takes a string that is the word to be completed and
662 # a state number, which should increment every time it's called.
664 return unless $connected;
666 my $line_buffer = $attribs->{line_buffer};
667 debugmsg(4, "line_buffer: [$line_buffer]");
669 if($line_buffer =~ /^\s*\@/) {
670 return($term->filename_completion_function(@_));
673 unless($completion_built) {
674 unless($opt_nocomp || !$conf{auto_complete}) {
675 populate_completion_list(1);
677 $completion_built = 1;
680 if($state == 0) {
681 # compute all the possibilies and put them in @completion_possibles
682 @completion_possibles = ();
683 my $last_char = substr($word,length($word)-1,1);
685 debugmsg(2,"last_char: [$last_char]");
687 my @grep = ();
688 if ($line_buffer =~ /select(?!.*(?:from|where))[\s\w\$\#_,]*\.[\w_]*$/) {
689 # This case is for "select mytable.mycolumn" type lines
690 my $current_table_name = $line_buffer;
691 $current_table_name =~ s/(select.*)(\s)([\w_]+)(\.)([\w_]*)$/$3/;
692 debugmsg(3, "current table name: $current_table_name");
694 unless($opt_nocomp || !$conf{auto_complete}) {
695 populate_completion_list(1, $current_table_name);
698 debugmsg(4, "select table.column");
700 push(@grep, '^current_column-');
701 } elsif($line_buffer =~ /select(?!.*(?:from|where))[\s\w\$\#_,]+$/) {
702 debugmsg(4, "select ...");
703 push(@grep, '^column-', '^table-');
704 } elsif($line_buffer =~ /from(?!.*where)[\s\w\$\#_,]*$/) {
705 debugmsg(4, "from ...");
706 push(@grep, '^table-');
707 } elsif($line_buffer =~ /where[\s\w\$\#_,]*$/) {
708 debugmsg(4, "where ...");
709 push(@grep, '^column-');
710 } elsif($line_buffer =~ /update(?!.*set)[\s\w\$\#_,]*$/) {
711 debugmsg(4, "where ...");
712 push(@grep, '^table-');
713 } elsif($line_buffer =~ /set[\s\w\$\#_,]*$/) {
714 debugmsg(4, "where ...");
715 push(@grep, '^column-');
716 } elsif($line_buffer =~ /insert.*into(?!.*values)[\s\w\$\#_,]*$/) {
717 debugmsg(4, "where ...");
718 push(@grep, '^table-');
719 } elsif($line_buffer =~ /^\s*show\s+(deps|dependencies)\s+\w+/) {
720 push(@grep, 'table-');
721 } elsif($line_buffer =~ /^\s*show\s\w*/) {
722 push(@grep, 'show-');
723 } else {
724 push(@grep, '');
726 debugmsg(2,"grep: [@grep]");
728 my $use_lower;
729 if($last_char =~ /^[A-Z]$/) {
730 $use_lower = 0;
731 } else {
732 $use_lower = 1;
734 foreach my $grep (@grep) {
735 foreach my $list_item (grep(/$grep/, @completion_list)) {
736 my $item = $list_item;
737 $item =~ s/^\w*-//;
738 eval { #Trap errors
739 if($item =~ /^\Q$word\E/i) {
740 push(@completion_possibles,
741 ($use_lower ? lc($item) : uc($item))
745 debugmsg(2, "Trapped error in complete_entry_function eval: $@") if $@;
748 debugmsg(3,"possibles: [@completion_possibles]");
751 # return the '$state'th element of the possibles
752 return($completion_possibles[$state] || undef);
755 sub db_reconnect {
756 debugmsg(3, "db_reconnect called", @_);
757 # This first disconnects the database, then tries to reconnect
759 print "Reconnecting...\n";
761 commit_on_exit();
763 if (defined $dbh) {
764 if (not $dbh->disconnect()) {
765 warn "Disconnect failed: $DBI::errstr\n";
766 return;
770 $dbh = db_connect(1, @dbparams);
773 sub db_connect {
774 my($die_on_error, $ora_session_mode, $username, $password, $connect_string) = @_;
775 debugmsg(3, "db_connect called", @_);
776 # Tries to connect to the database, prompting for username and password
777 # if not given. There are several cases that can happen:
778 # connect_string is present:
779 # ORACLE_HOME has to exist and the driver tries to make a connection to
780 # given connect_string.
781 # connect_string is not present:
782 # $opt_host is set:
783 # Connect to $opt_host on $opt_sid. Specify port only if $opt_port is
784 # set
785 # $opt_host is not set:
786 # Try to make connection to the default database by not specifying any
787 # host or connect string
789 my($dbhandle, $dberr, $dberrstr, $this_prompt_host, $this_prompt_user);
791 debugmsg(1,"ora_session_mode: [$ora_session_mode] username: [$username] password: [$password] connect_string: [$connect_string]");
793 # The first thing we're going to check is that the Oracle DBD is available
794 # since it's a sorta required element =)
795 my @drivers = DBI->available_drivers();
796 my $found = 0;
797 foreach(@drivers) {
798 if($_ eq "Oracle") {
799 $found = 1;
802 unless($found) {
803 lerr("Could not find DBD::Oracle... please install. Available drivers: "
804 .join(", ", @drivers) . ".\n");
806 #print "drivers: [" . join("|", @drivers) . "]\n";
808 # Now we can attempt a connection to the database
809 my $attributes = {
810 RaiseError => 0,
811 PrintError => 0,
812 AutoCommit => $conf{auto_commit},
813 LongReadLen => $conf{long_read_len},
814 LongTruncOk => $conf{long_trunc_ok},
815 ora_session_mode => $ora_session_mode
818 if($connect_string eq 'external') {
819 # the user wants to connect with external authentication
821 check_oracle_home();
823 # install alarm signal handle
824 $SIG{ALRM} = \&sighandle;
825 alarm($conf{connection_timeout});
827 if(!$opt_batch) {
828 print "Attempting connection to local database\n";
830 $dbhandle = DBI->connect('dbi:Oracle:',undef,undef,$attributes)
831 or do {
832 $dberr = $DBI::err;
833 $dberrstr = $DBI::errstr;
836 $this_prompt_host = $ENV{ORACLE_SID};
837 $this_prompt_user = $ENV{LOGNAME};
838 alarm(0); # cancel alarm
839 } elsif($connect_string) {
840 # We were provided with a connect string, so we can use the TNS method
842 check_oracle_home();
843 ($ora_session_mode, $username, $password) = get_up($ora_session_mode, $username, $password);
844 $attributes->{ora_session_mode} = $ora_session_mode if $ora_session_mode;
846 my $userstring;
847 if($username) {
848 $userstring = $username . '@' . $connect_string;
849 } else {
850 $userstring = $connect_string;
853 # install alarm signal handle
854 $SIG{ALRM} = \&sighandle;
855 alarm($conf{connection_timeout});
857 if(!$opt_batch) {
858 print "Attempting connection to $userstring\n";
860 $dbhandle = DBI->connect('dbi:Oracle:',$userstring,$password,$attributes)
861 or do {
862 $dberr = $DBI::err;
863 $dberrstr = $DBI::errstr;
866 $this_prompt_host = $connect_string;
867 $this_prompt_user = $username;
868 alarm(0); # cancel alarm
869 } elsif($opt_host) {
870 # attempt a connection to $opt_host
871 my $dsn;
872 $dsn = "host=$opt_host";
873 $dsn .= ";sid=$opt_sid" if $opt_sid;
874 $dsn .= ";port=$opt_port" if $opt_port;
876 ($ora_session_mode, $username, $password) = get_up($ora_session_mode, $username, $password);
877 $attributes->{ora_session_mode} = $ora_session_mode if $ora_session_mode;
879 # install alarm signal handle
880 $SIG{ALRM} = \&sighandle;
881 alarm($conf{connection_timeout});
883 print "Attempting connection to $opt_host\n";
884 debugmsg(1,"dsn: [$dsn]");
885 $dbhandle = DBI->connect("dbi:Oracle:$dsn",$username,$password,
886 $attributes)
887 or do {
888 $dberr = $DBI::err;
889 $dberrstr = $DBI::errstr;
892 $this_prompt_host = $opt_host;
893 $this_prompt_host = "$opt_sid!" . $this_prompt_host if $opt_sid;
894 $this_prompt_user = $username;
895 alarm(0); # cancel alarm
896 } else {
897 # attempt a connection without specifying a hostname or anything
899 check_oracle_home();
900 ($ora_session_mode, $username, $password) = get_up($ora_session_mode, $username, $password);
901 $attributes->{ora_session_mode} = $ora_session_mode if $ora_session_mode;
903 # install alarm signal handle
904 $SIG{ALRM} = \&sighandle;
905 alarm($conf{connection_timeout});
907 print "Attempting connection to local database\n";
908 $dbhandle = DBI->connect('dbi:Oracle:',$username,$password,$attributes)
909 or do {
910 $dberr = $DBI::err;
911 $dberrstr = $DBI::errstr;
914 $this_prompt_host = $ENV{ORACLE_SID};
915 $this_prompt_user = $username;
916 alarm(0); # cancel alarm
919 if($dbhandle) {
920 # Save the parameters for reconnecting
921 @dbparams = ($ora_session_mode, $username, $password, $connect_string);
923 # set the $dbuser global for use elsewhere
924 $dbuser = $username;
925 $num_connects = 0;
926 $prompt{host} = $this_prompt_host;
927 $prompt{user} = $this_prompt_user;
929 # Get the version banner
930 debugmsg(2,"Fetching version banner");
931 my $banner = $dbhandle->selectrow_array(
932 "select banner from v\$version where banner like 'Oracle%'");
933 if(!$opt_batch) {
934 if($banner) {
935 print "Connected to: $banner\n\n";
936 } else {
937 print "Connection successful!\n";
941 if($banner =~ / (\d+)\.(\d+)\.([\d\.]+)/) {
942 my ($major, $minor, $other) = ($1, $2, $3);
943 $dbversion = $major || 8;
946 # Issue a warning about autocommit. It's nice to know...
947 print STDERR "auto_commit is " . ($conf{auto_commit} ? "ON" : "OFF")
948 . ", commit_on_exit is " . ($conf{commit_on_exit} ? "ON" : "OFF")
949 . "\n" unless $opt_batch;
950 } elsif( ($dberr eq '1017' || $dberr eq '1005')
951 && ++$num_connects < $conf{max_connection_attempts}) {
952 $dberrstr =~ s/ \(DBD ERROR: OCISessionBegin\).*//;
953 print "Error: $dberrstr\n\n";
954 #@dbparams = (0,undef,undef,$connect_string);
955 $connect_string = '' if $connect_string eq 'external';
956 $dbhandle = db_connect($die_on_error,$ora_session_mode,undef,undef,$connect_string);
957 } elsif($die_on_error) {
958 lerr("Could not connect to database: $dberrstr [$dberr]");
959 } else {
960 wrn("Could not connect to database: $dberrstr [$dberr]");
961 return(0);
964 # set the NLS_DATE_FORMAT
965 if($conf{nls_date_format}) {
966 debugmsg(2, "setting NLS_DATE_FORMAT to $conf{nls_date_format}");
967 my $sqlstr = "alter session set nls_date_format = '"
968 . $conf{nls_date_format} . "'";
969 $dbhandle->do($sqlstr) or query_err('do', $DBI::errstr, $sqlstr);
972 $connected = 1;
973 return($dbhandle);
976 sub get_prompt {
977 my($prompt_string) = @_;
978 debugmsg(3, "get_prompt called", @_);
979 # This returns a prompt. It can be passed a string which will
980 # be manually put into the prompt. It will be padded on the left with
981 # white space
983 $prompt_length ||= 5; #just in case normal prompt hasn't been outputted
984 debugmsg(2, "prompt_length: [$prompt_length]");
986 if($prompt_string) {
987 my $temp_prompt = sprintf('%' . $prompt_length . 's', $prompt_string . '> ');
988 return($temp_prompt);
989 } else {
990 my $temp_prompt = $conf{prompt} . '> ';
991 my $temp_prompt_host = '@' . $prompt{host} if $prompt{host};
992 $temp_prompt =~ s/\%H/$temp_prompt_host/g;
993 $temp_prompt =~ s/\%U/$prompt{user}/g;
995 $prompt_length = length($temp_prompt);
996 return($temp_prompt);
1000 sub get_up {
1001 my($ora_session_mode, $username, $password) = @_;
1002 debugmsg(3, "get_up called", @_);
1004 if(!$opt_batch) {
1006 setup_term() unless $term;
1008 # Get username/password
1009 unless($username) {
1010 # prompt for the username
1011 $username = $term->readline('Username: ');
1012 if($username =~ /^(.*)\s+as\s+sys(\w+)\s*$/i) {
1013 $ora_session_mode = 2 if lc($2) eq 'dba';
1014 $ora_session_mode = 4 if lc($2) eq 'oper';
1015 $username = $1;
1018 # Take that entry off of the history list
1019 if ($term_type eq 'gnu') {
1020 $term->remove_history($term->where_history());
1024 unless($password) {
1025 # prompt for the password, and disable echo
1026 my $orig_redisplay = $attribs->{redisplay_function};
1027 $attribs->{redisplay_function} = \&shadow_redisplay;
1029 $password = $term->readline('Password: ');
1031 $attribs->{redisplay_function} = $orig_redisplay;
1033 # Take that entry off of the history list
1034 if ($term->ReadLine eq "Term::ReadLine::Gnu") {
1035 $term->remove_history($term->where_history());
1040 return($ora_session_mode, $username, $password);
1044 sub check_oracle_home {
1045 # This checks for the ORACLE_HOME environment variable and dies if it's
1046 # not set
1047 lerr("Please set your ORACLE_HOME environment variable!")
1048 unless $ENV{ORACLE_HOME};
1049 return(1);
1052 sub shadow_redisplay {
1053 # The one provided in Term::ReadLine::Gnu was broken
1054 # debugmsg(2, "shadow_redisplay called", @_);
1055 my $OUT = $attribs->{outstream};
1056 my $oldfh = select($OUT); $| = 1; select($oldfh);
1057 print $OUT ("\r", $attribs->{prompt});
1058 $oldfh = select($OUT); $| = 0; select($oldfh);
1061 sub print_non_print {
1062 my($string) = @_;
1064 my @string = unpack("C*", $string);
1065 my $ret_string;
1066 foreach(@string) {
1067 if($_ >= 40 && $_ <= 176) {
1068 $ret_string .= chr($_);
1069 } else {
1070 $ret_string .= "<$_>";
1073 return($ret_string);
1076 sub interface {
1077 debugmsg(3, "interface called", @_);
1078 # this is the main program loop that handles all the user input.
1079 my $input;
1080 my $prompt = get_prompt();
1082 setup_sigs();
1084 # Check if we were interactively called, or do we need to process STDIN
1085 if(-t STDIN) {
1086 while(defined($input = $term->readline($prompt))) {
1087 $sigintcaught = 0;
1088 $prompt = process_input($input, $prompt) || get_prompt();
1089 setup_sigs();
1091 } else {
1092 debugmsg(3, "non-interactive", @_);
1093 debugmsg(3, "\$opt_batch=$opt_batch", @_);
1094 debugmsg(3, "\$opt_batch=$opt_batch", @_);
1095 # Send STDIN to process_input();
1096 while(<STDIN>) {
1097 process_input($_);
1101 quit(0, undef, "\n");
1104 sub process_input {
1105 my($input, $prompt, $add_to_history) = @_;
1106 if (!(defined($add_to_history))) {
1107 $add_to_history = 1;
1109 debugmsg(3, "process_input called", @_);
1111 my $nprompt;
1112 SWITCH: {
1113 if(!$qbuffer) {
1114 # Commands that are only allowed if there is no current buffer
1115 $input =~ /^\s*(?:!|host)\s*(.*)\s*$/i and system($1), last SWITCH;
1116 $input =~ /^\s*\\a\s*$/i and populate_completion_list(), last SWITCH;
1117 $input =~ /^\s*\\\?\s*$/i and help(), last SWITCH;
1118 $input =~ /^\s*help\s*$/i and help(), last SWITCH;
1119 $input =~ /^\s*reconnect\s*$/i and db_reconnect(), last SWITCH;
1120 $input =~ /^\s*\\r\s*$/i and db_reconnect(), last SWITCH;
1121 $input =~ /^\s*conn(?:ect)?\s+(.*)$/i and connect_cmd($1), last SWITCH;
1122 $input =~ /^\s*disc(?:onnect)\s*$/i and disconnect_cmd($1), last SWITCH;
1123 $input =~ /^\s*\@\S+\s*$/i and $nprompt = run_script($input), last SWITCH;
1124 $input =~ /^\s*debug\s*(.*)$/i and debug_toggle($1), last SWITCH;
1125 $input =~ /^\s*autocommit\s*(.*)$/i and autocommit_toggle(), last SWITCH;
1126 $input =~ /^\s*commit/i and commit_cmd(), last SWITCH;
1127 $input =~ /^\s*rollback/i and rollback_cmd(), last SWITCH;
1128 $input =~ /^\s*(show\s*[^;\/\\]+)\s*$/i and show($1, 'table'),last SWITCH;
1129 $input =~ /^\s*(desc\s*[^;\/\\]+)\s*$/i and describe($1, 'table'),
1130 last SWITCH;
1131 $input =~ /^\s*(set\s*[^;\/\\]+)\s*$/i and set_cmd($1), last SWITCH;
1132 $input =~ /^\s*(let\s*[^;\/\\]*)\s*$/i and let_cmd($1), last SWITCH;
1133 $input =~ /^\s*exec(?:ute)?\s*(.*)\s*$/i and exec_cmd($1), last SWITCH;
1134 $input =~ /^\s*\\d\s*$/ and show('show objects', 'table'), last SWITCH;
1135 $input =~ /^\s*\\dt\s*$/ and show('show tables', 'table'), last SWITCH;
1136 $input =~ /^\s*\\di\s*$/ and show('show indexes', 'table'), last SWITCH;
1137 $input =~ /^\s*\\ds\s*$/ and show('show sequences', 'table'), last SWITCH;
1138 $input =~ /^\s*\\dv\s*$/ and show('show views', 'table'), last SWITCH;
1139 $input =~ /^\s*\\df\s*$/ and show('show functions', 'table'), last SWITCH;
1141 # Global commands allowed any time (even in the middle of queries)
1142 $input =~ /^\s*quit\s*$/i and quit(0), last SWITCH;
1143 $input =~ /^\s*exit\s*$/i and quit(0), last SWITCH;
1144 $input =~ /^\s*\\q\s*$/i and quit(0), last SWITCH;
1145 $input =~ /^\s*\\l\s*$/i and show_qbuffer(), last SWITCH;
1146 $input =~ /^\s*\\p\s*$/i and show_qbuffer(), last SWITCH;
1147 $input =~ /^\s*l\s*$/i and show_qbuffer(), last SWITCH;
1148 $input =~ /^\s*list\s*$/i and show_qbuffer(), last SWITCH;
1149 $input =~ /^\s*\\c\s*$/i and $nprompt = clear_qbuffer(), last SWITCH;
1150 $input =~ /^\s*clear\s*$/i and $nprompt = clear_qbuffer(), last SWITCH;
1151 $input =~ /^\s*clear buffer\s*$/i and $nprompt=clear_qbuffer(), last SWITCH;
1152 $input =~ /^\s*\\e\s*(.*)$/i and $nprompt = edit($1), last SWITCH;
1153 $input =~ /^\s*edit\s*(.*)$/i and $nprompt = edit($1), last SWITCH;
1154 $input =~ /^\s*rem(?:ark)?/i and $input = '', last SWITCH;
1155 $input =~ /[^\s]/ and $nprompt = parse_input($input) || last, last SWITCH;
1157 # default
1158 $nprompt = $prompt if ($nprompt eq ''); # use last prompt if nothing caught (blank line)
1160 if(!$opt_batch && $term->ReadLine eq "Term::ReadLine::Gnu" && $input =~ /[^\s]/ &&
1161 $input ne $last_history) {
1162 if (!$opt_batch && $add_to_history) {
1163 $term->AddHistory($input);
1166 $last_history = $input;
1167 return($nprompt);
1170 sub parse_input {
1171 my($input) = @_;
1172 debugmsg(3, "parse_input called", @_);
1173 # this takes input and parses it. It looks for single quotes (') and double
1174 # quotes (") and presents prompts accordingly. It also looks for query
1175 # terminators, such as semicolon (;), forward-slash (/) and back-slash-g (\g).
1176 # If it finds a query terminator, then it pushes any text onto the query
1177 # buffer ($qbuffer) and then passes the entire query buffer, as well as the
1178 # format type, determined by the terminator type, to the query() function. It
1179 # also wipes out the qbuffer at this time.
1181 # It returns a prompt (like 'SQL> ' or ' -> ') if successfull, 0 otherwise
1183 # now we need to check for a terminator, if we're not inquotes
1184 while( $input =~ m/
1186 ['"] # match quotes
1187 | # or
1188 ; # the ';' terminator
1189 | # or
1190 ^\s*\/\s*$ # the slash terminator at end of string
1191 | # or
1192 \\[GgsSi] # one of the complex terminators
1193 | # or
1194 (?:^|\s+)create\s+ # create
1195 | # or
1196 (?:^|\s+)function\s+ # function
1197 | # or
1198 (?:^|\s+)package\s+ # package
1199 | # or
1200 (?:^|\s+)package\s+body\s+ # package body
1201 | # or
1202 (?:^|\s+)procedure\s+ # procedure
1203 | # or
1204 (?:^|\s+)trigger\s+ # trigger
1205 | # or
1206 (?:^|\s+)declare\s+ # declare
1207 | # or
1208 (?:^|\s+)begin\s+ # begin
1209 | # or
1210 \/\* # start of multiline comment
1211 | # or
1212 \*\/ # end of multiline comment
1213 )/gix )
1216 my($pre, $match, $post) = ($`, $1, $');
1217 # PREMATCH, MATCH, POSTMATCH
1218 debugmsg(1, "parse: [$pre] [$match] [$post]");
1220 if( ($match eq '\'' || $match eq '"')) {
1221 if(!$quote || $quote eq $match) {
1222 $inquotes = ($inquotes ? 0 : 1);
1223 if($inquotes) {
1224 $quote = $match;
1225 } else {
1226 undef($quote);
1229 } elsif($match =~ /create/ix) {
1230 $increate = 1;
1231 } elsif(!$increate &&
1232 $match =~ /function|package|package\s+body|procedure|trigger/ix)
1234 # do nothing if we're not in a create statement
1235 } elsif(($match =~ /declare|begin/ix) ||
1236 ($increate && $match =~ /function|package|package\s+body|procedure|trigger/ix))
1238 $inplsqlblock = 1;
1239 } elsif($match =~ /^\/\*/) {
1240 $incomment = 1;
1241 } elsif($match =~ /^\*\//) {
1242 $incomment = 0;
1243 } elsif(!$inquotes && !$incomment && $match !~ /^--/ &&
1244 ($match =~ /^\s*\/\s*$/ || !$inplsqlblock))
1246 $qbuffer .= $pre;
1247 debugmsg(4,"qbuffer IN: [$qbuffer]");
1248 my $terminator = $match;
1249 $post =~ / (\d*) # Match num_rows right after terminitor
1250 \s* # Optional whitespace
1251 (?: #
1252 ( >{1,2}|<|\| ) # Match redirection operators
1253 \s* # Optional whitespace
1254 ( .* ) # The redirector (include rest of line)
1255 )? # Match 0 or 1
1256 \s* # Optional whitespace
1257 (.*) # Catch everything else
1258 $ # End-Of-Line
1260 debugmsg(3,"1: [$1] 2: [$2] 3: [$3] 4: [$4]");
1262 my($num_rows,$op,$op_text,$extra) = ($1,$2,$3,$4);
1264 if($extra =~ /--.*$/) {
1265 undef $extra;
1268 # check that Text::CSV_XS is installed if a < redirection was given
1269 if($op eq '<' && $notextcsv) {
1270 soft_err("You must install Text::CSV_XS from CPAN to use this feature");
1271 return(0);
1274 # deduce the format from the terminator type
1275 my $format;
1277 $fbuffer = $terminator;
1279 if($terminator eq ';' || $terminator =~ /^\/\s*$/) {
1280 $format = 'table';
1281 } elsif($terminator eq '\g') {
1282 $format = 'list';
1283 } elsif($terminator eq '\G') {
1284 $format = 'list_aligned';
1285 } elsif($terminator eq '\s') {
1286 $format = 'csv';
1287 } elsif($terminator eq '\S') {
1288 $format = 'csv_no_header';
1289 } elsif($terminator eq '\i') {
1290 $format = 'sql';
1292 $num_rows ||= 0;
1294 debugmsg(4,"fbuffer: [$fbuffer]\n");
1296 # if there is nothing in the buffer, then we assume that the user just
1297 # wants to reexecute the last query, which we have saved in $last_qbuffer
1298 my($use_buffer, $copy_buffer);
1299 if($qbuffer) {
1300 $use_buffer = $qbuffer;
1301 $copy_buffer = 1;
1302 } elsif($last_qbuffer) {
1303 $use_buffer = $last_qbuffer;
1304 $copy_buffer = 0;
1305 } else {
1306 $use_buffer = undef;
1307 $copy_buffer = 0;
1310 if($use_buffer) {
1311 if($op eq '<') {
1312 my $count = 0;
1313 my($max_lines, @params, $max_lines_save, @querybench,
1314 $rows_affected, $success_code);
1315 my $result_output = 1;
1316 push(@querybench, get_bench());
1317 print STDERR "\n";
1318 while(($max_lines, @params) = get_csv_file($op, $op_text)) {
1319 $max_lines_save = $max_lines;
1320 print statusline($count, $max_lines);
1322 my @res = query( $use_buffer, $format,
1323 {num_rows => $num_rows, op => $op, op_text => $op_text,
1324 result_output => 0}, @params);
1326 debugmsg(3, "res: [@res]");
1328 unless(@res) {
1329 print "Error in line " . ($count + 1) . " of file '$op_text'\n";
1330 $result_output = 0;
1331 close_csv();
1332 last;
1335 $rows_affected += $res[0];
1336 $success_code = $res[1];
1337 $count++;
1339 push(@querybench, get_bench());
1341 if($result_output) {
1342 print "\r\e[K";
1344 if(!$opt_batch) {
1345 print STDERR format_affected($rows_affected, $success_code);
1346 if($opt_bench || $conf{extended_benchmarks}) {
1347 print STDERR "\n\n";
1348 print STDERR ('-' x 80);
1349 print STDERR "\n";
1350 output_benchmark("Query: ", @querybench, "\n");
1351 } else {
1352 output_benchmark(" (", @querybench, ")");
1353 print STDERR "\n";
1355 print STDERR "\n";
1358 } else {
1359 query($use_buffer, $format, {num_rows => $num_rows, op => $op,
1360 op_text => $op_text});
1363 if($copy_buffer) {
1364 # copy the current qbuffer to old_qbuffer
1365 $last_qbuffer = $qbuffer;
1366 $last_fbuffer = $fbuffer;
1368 } else {
1369 query_err('Query', 'No current query in buffer');
1372 undef($qbuffer);
1373 undef($fbuffer);
1374 $inplsqlblock = 0;
1375 $increate = 0;
1377 if($extra) {
1378 return(parse_input($extra));
1379 } else {
1380 # return a 'new' prompt
1381 return(get_prompt());
1386 $qbuffer .= $input . "\n";
1388 debugmsg(4,"qbuffer: [$qbuffer], input: [$input]");
1390 if($inquotes) {
1391 return(get_prompt($quote));
1392 } elsif($incomment) {
1393 return(get_prompt('DOC'));
1394 } else {
1395 return(get_prompt('-'));
1399 sub get_csv_file {
1400 my($op, $op_text) = @_;
1401 debugmsg(3, "get_csv_file called", @_);
1403 my @ret = ();
1405 unless($csv_max_lines) {
1406 ($op_text) = glob($op_text);
1407 debugmsg(3, "Opening file '$op_text' for line counting");
1408 open(CSV, $op_text) || do{
1409 query_err('redirect',"Cannot open file '$op_text' for reading: $!");
1410 return();
1412 while(<CSV>) {
1413 $csv_max_lines++;
1415 close(CSV);
1418 unless($csv_filehandle_open) {
1419 ($op_text) = glob($op_text);
1420 debugmsg(3, "Opening file '$op_text' for input");
1421 open(CSV, $op_text) || do{
1422 query_err('redirect',"Cannot open file '$op_text' for reading: $!");
1423 return();
1425 $csv_filehandle_open = 1;
1428 my $line = <CSV>;
1429 while(defined($line) && $line =~ /^\s*$/) {
1430 $line = <CSV>;
1433 unless($line) {
1434 close_csv();
1435 return();
1438 debugmsg(3, "read in CSV line", $line);
1440 my @fields;
1441 if($csv->parse($line)) {
1442 @fields = $csv->fields();
1443 debugmsg(3, "got CVS fields", @fields);
1444 } else {
1445 wrn("Parse of CSV file failed on argument, skipping to next: "
1446 . $csv->error_input());
1447 return(get_csv_file($op, $op_text));
1450 return($csv_max_lines, @fields);
1453 sub close_csv {
1454 close(CSV) || lerr("Could not close CSV filehandle: $!");
1455 $csv_filehandle_open = 0;
1456 $csv_max_lines = 0;
1459 sub connect_cmd {
1460 my($arg) = @_;
1461 debugmsg(3, "connect_cmd called", @_);
1463 unless($arg) {
1464 wrn("Invalid connect syntax. See help");
1465 return(0);
1468 my($ora_session_mode, $username, $password, $connect_string) = parse_logon_string($arg);
1470 my $new_dbh = db_connect(0, $ora_session_mode, $username, $password, $connect_string);
1471 if (not $new_dbh) {
1472 warn "failed to make new connection as $username to $connect_string: $DBI::errstr\n";
1473 warn "keeping old connection\n";
1474 return;
1477 if (defined $dbh) {
1478 commit_on_exit();
1479 $dbh->disconnect()
1480 or warn "failed to disconnect old connection - switching anyway\n";
1483 $dbh = $new_dbh;
1484 $connected = 1;
1487 sub disconnect_cmd {
1488 debugmsg(3, "disconnect_cmd called", @_);
1490 if ($connected) {
1491 print "Closing last connection...\n";
1492 commit_on_exit();
1494 $dbh->disconnect() if (defined $dbh);
1495 $connected = 0;
1496 } else {
1497 print "Not connected.\n";
1501 sub commit_cmd {
1502 debugmsg(3, "commit_cmd called", @_);
1503 # this just called commit
1505 if(defined $dbh) {
1506 if($dbh->{AutoCommit}) {
1507 wrn("commit ineffective with AutoCommit enabled");
1508 } else {
1509 if ($dbh->commit()) {
1510 print "Transaction committed\n";
1512 else {
1513 warn "Commit failed: $DBI::errstr\n";
1516 } else {
1517 print "No connection\n";
1521 sub rollback_cmd {
1522 debugmsg(3, "rollback_cmd called", @_);
1523 # this just called commit
1525 if(defined $dbh) {
1526 if($dbh->{AutoCommit}) {
1527 wrn("rollback ineffective with AutoCommit enabled");
1528 } else {
1529 if ($dbh->rollback()) {
1530 print "Transaction rolled back\n";
1532 else {
1533 warn "Rollback failed: $DBI::errstr\n";
1536 } else {
1537 print "No connection\n";
1541 sub exec_cmd {
1542 my($sqlstr) = @_;
1543 debugmsg(3, "exec_cmd called", @_);
1544 # Wrap the statement in BEGIN/END and execute
1546 $sqlstr = qq(
1547 BEGIN
1548 $sqlstr
1549 END;
1552 query($sqlstr, 'table');
1555 sub edit {
1556 my($filename) = @_;
1557 debugmsg(3, "edit called", @_);
1558 # This writes the current qbuffer to a file then opens up an editor on that
1559 # file... when the editor returns, we read in the file and overwrite the
1560 # qbuffer with it. If there is nothing in the qbuffer, and there is
1561 # something in the last_qbuffer, then we use the last_qbuffer. If nothing
1562 # is in either, then we just open the editor with a blank file.
1564 my $passed_file = 1 if $filename;
1565 my $filecontents;
1566 my $prompt = get_prompt();
1568 debugmsg(2, "passed_file: [$passed_file]");
1570 if($qbuffer) {
1571 debugmsg(2, "Using current qbuffer for contents");
1572 $filecontents = $qbuffer;
1573 } elsif($last_qbuffer) {
1574 debugmsg(2, "Using last_qbuffer for contents");
1575 $filecontents = $last_qbuffer . $last_fbuffer;
1576 } else {
1577 debugmsg(2, "Using blank contents");
1578 $filecontents = "";
1581 debugmsg(3, "filecontents: [$filecontents]");
1583 # determine the tmp directory
1584 my $tmpdir;
1585 if($ENV{TMP}) {
1586 $tmpdir = $ENV{TMP};
1587 } elsif($ENV{TEMP}) {
1588 $tmpdir = $ENV{TEMP};
1589 } elsif(-d "/tmp") {
1590 $tmpdir = "/tmp";
1591 } else {
1592 $tmpdir = ".";
1595 # determine the preferred editor
1596 my $editor;
1597 if($ENV{EDITOR}) {
1598 $editor = $ENV{EDITOR};
1599 } else {
1600 $editor = "vi";
1603 # create the filename, if not given one
1604 $filename ||= "$tmpdir/yasql_" . int(rand(1000)) . "_$$.sql";
1606 # expand the filename
1607 ($filename) = glob($filename);
1609 debugmsg(1, "Editing $filename with $editor");
1611 # check for file existance. If it exists, then we open it up but don't
1612 # write the buffer to it
1613 my $file_exists;
1614 if($passed_file) {
1615 # if the file was passed, then check for it's existance
1616 if(-e $filename) {
1617 # The file was found
1618 $file_exists = 1;
1619 } elsif(-e "$filename.sql") {
1620 # the file was found with a .sql extension
1621 $filename = "$filename.sql";
1622 $file_exists = 1;
1623 } else {
1624 wrn("$filename was not found, creating new file, which will not be ".
1625 "deleted");
1627 } else {
1628 # no file was specified, so just write to the the temp file, and we
1629 # don't care if it exists, since there's no way another process could
1630 # write to the same file at the same time since we use the PID in the
1631 # filename.
1632 my $ret = open(TMPFILE, ">$filename");
1633 if(!$ret) { #if file was NOT opened successfully
1634 wrn("Could not write to $filename: $!");
1635 } else {
1636 print TMPFILE $filecontents;
1637 close(TMPFILE);
1641 # now spawn the editor
1642 my($ret, @filecontents);
1643 debugmsg(2, "Executing $editor $filename");
1644 $ret = system($editor, "$filename");
1645 if($ret) {
1646 debugmsg(2, "Executing env $editor $filename");
1647 $ret = system("env", $editor, "$filename");
1649 if($ret) {
1650 debugmsg(2, "Executing `which $editor` $filename");
1651 $ret = system("`which $editor`", "$filename");
1654 if($ret) { #if the editor or system returned a positive return value
1655 wrn("Editor exited with $ret: $!");
1656 } else {
1657 # read in the tmp file and apply it's contents to the buffer
1658 my $ret = open(TMPFILE, "$filename");
1659 if(!$ret) { # if file was NOT opened successfully
1660 wrn("Could not read $filename: $!");
1661 } else {
1662 # delete our qbuffer and reset the inquotes var
1663 $qbuffer = "";
1664 $inquotes = 0;
1665 $increate = 0;
1666 $inplsqlblock = 0;
1667 $incomment = 0;
1668 while(<TMPFILE>) {
1669 push(@filecontents, $_);
1671 close(TMPFILE);
1675 if(@filecontents) {
1676 print "\n";
1677 print join('', @filecontents);
1678 print "\n";
1680 foreach my $line (@filecontents) {
1681 # chomp off newlines
1682 chomp($line);
1684 last if $sigintcaught;
1685 # now send it in to process_input
1686 # and don't add lines of the script to command history
1687 $prompt = process_input($line, '', 0);
1691 unless($passed_file) {
1692 # delete the tmp file
1693 debugmsg(1, "Deleting $filename");
1694 unlink("$filename") ||
1695 wrn("Could not unlink $filename: $!");
1698 return($prompt);
1701 sub run_script {
1702 my($input) = @_;
1703 debugmsg(3, "run_script called", @_);
1704 # This reads in the given script and executes it's lines as if they were typed
1705 # in directly. It will NOT erase the current buffer before it runs. It
1706 # will append the contents of the file to the current buffer, basicly
1708 my $prompt;
1710 # parse input
1711 $input =~ /^\@(.*)$/;
1712 my $file = $1;
1713 ($file) = glob($file);
1714 debugmsg(2, "globbed [$file]");
1716 my $first_char = substr($file, 0, 1);
1717 unless($first_char eq '/' or $first_char eq '.') {
1718 foreach my $path ('.', @sqlpath) {
1719 if(-e "$path/$file") {
1720 $file = "$path/$file";
1721 last;
1722 } elsif(-e "$path/$file.sql") {
1723 $file = "$path/$file.sql";
1724 last;
1728 debugmsg(2, "Found [$file]");
1730 # read in the tmp file and apply it's contents to the buffer
1731 my $ret = open(SCRIPT, $file);
1732 if(!$ret) { # if file was NOT opened successfully
1733 wrn("Could not read $file: $!");
1734 $prompt = get_prompt();
1735 } else {
1736 # read in the script
1737 while(<SCRIPT>) {
1738 # chomp off newlines
1739 chomp;
1741 last if $sigintcaught;
1743 # now send it in to process_input
1744 # and don't add lines of the script to command history
1745 $prompt = process_input($_, '', 0);
1747 close(SCRIPT);
1750 return($prompt);
1753 sub show_qbuffer {
1754 debugmsg(3, "show_qbuffer called", @_);
1755 # This outputs the current buffer
1757 #print "\nBuffer:\n";
1758 if($qbuffer) {
1759 print $qbuffer;
1760 } else {
1761 print STDERR "Buffer empty";
1763 print "\n";
1766 sub clear_qbuffer {
1767 debugmsg(3, "clear_qbuffer called", @_);
1768 # This clears the current buffer
1770 $qbuffer = '';
1771 $inquotes = 0;
1772 $inplsqlblock = 0;
1773 $increate = 0;
1774 $incomment = 0;
1775 print "Buffer cleared\n";
1776 return(get_prompt());
1779 sub debug_toggle {
1780 my($debuglevel) = @_;
1781 debugmsg(3, "debug_toggle called", @_);
1782 # If nothing is passed, then debugging is turned off if on, on if off. If
1783 # a number is passed, then we explicitly set debugging to that number
1786 if(length($debuglevel) > 0) {
1787 unless($debuglevel =~ /^\d+$/) {
1788 wrn('Debug level must be an integer');
1789 return(1);
1792 $opt_debug = $debuglevel;
1793 } else {
1794 if($opt_debug) {
1795 $opt_debug = 0;
1796 } else {
1797 $opt_debug = 1;
1800 $opt_debug > 3 ? DBI->trace(1) : DBI->trace(0);
1801 print "** debug is now " . ($opt_debug ? "level $opt_debug" : 'off') . "\n";
1804 sub autocommit_toggle {
1805 debugmsg(3, "autocommit_toggle called", @_);
1806 # autocommit is turned off if on on if off
1808 if($dbh->{AutoCommit}) {
1809 $dbh->{AutoCommit} = 0;
1810 } else {
1811 $dbh->{AutoCommit} = 1;
1814 print "AutoCommit is now " . ($dbh->{AutoCommit} ? 'on' : 'off') . "\n";
1817 sub show_all_query {
1818 my ( $select, $order_by, $format, $opts, $static_where , $option, $option_key, @values ) = @_;
1819 debugmsg(3, "show_all_query called");
1820 my $where = ' where ';
1821 if ( $static_where ) {
1822 $where = ' where '. $static_where . ' ';
1825 if ( $option eq 'like' ){
1826 my $sqlstr = $select . $where;
1827 $sqlstr .= ' and ' if ( $static_where );
1828 $sqlstr .= $option_key ." like ? " . $order_by;
1830 query($sqlstr , $format, $opts, @values );
1831 }else{
1832 my $sqlstr = $select;
1833 $sqlstr .= $where if ($static_where);
1834 $sqlstr .= $order_by;
1836 query($sqlstr , $format, $opts );
1841 sub show {
1842 my($input, $format, $num_rows, $op, $op_text) = @_;
1843 debugmsg(3, "show called", @_);
1844 # Can 'show thing'. Possible things:
1845 # tables - outputs all of the tables that the current user owns
1846 # sequences - outputs all of the sequences that the current user owns
1848 # Can also 'show thing on table'. Possible things:
1849 # constraints - Shows constraints on the 'table', like Check, Primary Key,
1850 # Unique, and Foreign Key
1851 # indexes - Shows indexes on the 'table'
1852 # triggers - Shows triggers on the 'table'
1854 # convert to lowercase for comparison operations
1855 $input = lc($input);
1857 # drop trailing whitespaces
1858 ($input = $input) =~ s/( +)$//;
1860 # parse the input to find out what 'thing' has been requested
1861 if($input =~ /^\s*show\s+([a-zA-Z0-9_\$\#\s]+)\s+(?:on|for)\s+([a-zA-Z0-9_\$\#]+)/) {
1862 # this is a thing on a table
1863 if($1 eq 'indexes') {
1864 my $sqlstr;
1865 if($dbversion >= 8) {
1866 $sqlstr = q{
1867 select ai.index_name "Index Name",
1868 ai.index_type "Type",
1869 ai.uniqueness "Unique?",
1870 aic.column_name "Column Name"
1871 from all_indexes ai, all_ind_columns aic
1872 where ai.index_name = aic.index_name
1873 and ai.table_owner = aic.table_owner
1874 and ai.table_name = ?
1875 and ai.table_owner = ?
1876 order by ai.index_name, aic.column_position
1878 } else {
1879 $sqlstr = q{
1880 select ai.index_name "Index Name",
1881 ai.uniqueness "Unique?",
1882 aic.column_name "Column Name"
1883 from all_indexes ai, all_ind_columns aic
1884 where ai.index_name = aic.index_name
1885 and ai.table_owner = aic.table_owner
1886 and ai.table_name = ?
1887 and ai.table_owner = ?
1888 order by ai.index_name, aic.column_position
1891 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1892 op_text => $op_text}, uc($2), uc($dbuser));
1893 } elsif($1 eq 'constraints') {
1894 my $sqlstr = q{
1895 select constraint_name "Constraint Name",
1896 decode(constraint_type,
1897 'C', 'Check',
1898 'P', 'Primary Key',
1899 'R', 'Foreign Key',
1900 'U', 'Unique',
1901 '') "Type",
1902 search_condition "Search Condition"
1903 from all_constraints
1904 where table_name = ?
1905 and owner = ?
1906 order by constraint_name
1908 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1909 op_text => $op_text}, uc($2), uc($dbuser));
1910 } elsif($1 eq 'keys') {
1911 my $sqlstr = q{
1912 select ac.constraint_name "Name",
1913 decode(ac.constraint_type,
1914 'R', 'Foreign Key',
1915 'U', 'Unique',
1916 'P', 'Primary Key',
1917 ac.constraint_type) "Type",
1918 ac.table_name "Table Name",
1919 acc.column_name "Column",
1920 r_ac.table_name "Parent Table",
1921 r_acc.column_name "Parent Column"
1922 from all_constraints ac, all_cons_columns acc,
1923 all_constraints r_ac, all_cons_columns r_acc
1924 where ac.constraint_name = acc.constraint_name
1925 and ac.owner = acc.owner
1926 and ac.constraint_type in ('R','U','P')
1927 and ac.r_constraint_name = r_ac.constraint_name(+)
1928 and r_ac.constraint_name = r_acc.constraint_name(+)
1929 and r_ac.owner = r_acc.owner(+)
1930 and ac.table_name = ?
1931 and ac.owner = ?
1932 order by ac.constraint_name, acc.position
1934 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1935 op_text => $op_text}, uc($2), uc($dbuser));
1936 } elsif($1 eq 'checks') {
1937 my $sqlstr = q{
1938 select ac.constraint_name "Name",
1939 decode(ac.constraint_type,
1940 'C', 'Check',
1941 ac.constraint_type) "Type",
1942 ac.table_name "Table Name",
1943 ac.search_condition "Search Condition"
1944 from all_constraints ac
1945 where ac.table_name = ?
1946 and ac.constraint_type = 'C'
1947 and ac.owner = ?
1948 order by ac.constraint_name
1950 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1951 op_text => $op_text}, uc($2), uc($dbuser));
1952 } elsif($1 eq 'triggers') {
1953 my $sqlstr = q{
1954 select trigger_name "Trigger Name",
1955 trigger_type "Type",
1956 when_clause "When",
1957 triggering_event "Event"
1958 from all_triggers
1959 where table_name = ?
1960 and owner = ?
1962 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1963 op_text => $op_text}, uc($2), uc($dbuser));
1964 } elsif($1 eq 'query') {
1965 my $sqlstr = q{
1966 select count(*) from all_mviews where mview_name = ? and owner = ?
1968 my $is_mview = $dbh->selectrow_array($sqlstr, undef, uc($2), uc($dbuser));
1969 if($is_mview) {
1970 $sqlstr = q{
1971 select query
1972 from all_mviews
1973 where mview_name = ?
1974 and owner = ?
1976 } else {
1977 $sqlstr = q{
1978 select text
1979 from all_views
1980 where view_name = ?
1981 and owner = ?
1984 my $prev_LongReadLen = $dbh->{LongReadLen};
1985 $dbh->{LongReadLen} = 8000;
1986 query($sqlstr, 'single_output', {num_rows => $num_rows, op => $op,
1987 op_text => $op_text}, uc($2), uc($dbuser));
1988 $dbh->{LongReadLen} = $prev_LongReadLen;
1989 } elsif($1 eq 'deps' || $1 eq 'dependencies') {
1990 my $table = $2;
1991 my $sqlstr = q{
1992 select
1993 column_name "Column Name"
1994 , type "Type"
1995 , tablett || '(' || pk || ')' "Reference"
1996 , constraint_name "Constraint"
1997 , delete_rule "On delete"
1998 from (
1999 select
2000 a.owner,
2001 a.table_name,
2002 b.column_name,
2003 c.owner || '.' || c.table_name tablett,
2004 d.column_name pk,
2005 a.constraint_name,
2006 a.delete_rule,
2007 'parent ->' type
2008 from all_constraints a,
2009 all_cons_columns b,
2010 all_constraints c,
2011 all_cons_columns d
2012 where a.constraint_name = b.constraint_name
2013 and a.r_constraint_name is not null
2014 and a.r_constraint_name=c.constraint_name
2015 and c.constraint_name=d.constraint_name
2016 and a.owner = b.owner and c.owner = d.owner
2017 UNION
2018 SELECT
2019 a.owner,
2020 a.table_name parent_table,
2021 b.column_name,
2022 c.owner || '.' || c.table_name tablett,
2023 d.column_name pk,
2024 c.constraint_name,
2025 c.delete_rule,
2026 'child <-' as type
2027 FROM all_constraints a,
2028 all_cons_columns b,
2029 all_constraints c,
2030 all_cons_columns d
2031 WHERE a.constraint_name = b.constraint_name
2032 AND a.constraint_name = c.r_constraint_name
2033 AND c.constraint_name = d.constraint_name
2034 and a.owner = b.owner and c.owner = d.owner
2035 ) where table_name like ?
2036 and owner like ?
2037 ORDER BY 2,1,3,4
2039 query($sqlstr, 'table', {num_rows => $num_rows, op => $op,
2040 op_text => $op_text}, uc($table), uc($dbuser));
2041 } elsif($1 eq 'ddl') {
2042 my $object_name = $2;
2043 my $object_type = get_object_type($object_name);
2045 my $prev_LongReadLen = $dbh->{LongReadLen};
2046 $dbh->{LongReadLen} = 16_000;
2048 if ( $object_type eq 'TABLE'){
2049 my $sqlstr = q{
2050 SELECT DBMS_METADATA.GET_DDL('TABLE', ?, ?) FROM dual
2051 union all
2052 SELECT DBMS_METADATA.GET_DEPENDENT_DDL('INDEX', ?, ?) FROM dual
2053 union all
2054 SELECT DBMS_METADATA.GET_DEPENDENT_DDL ('COMMENT', ?, ?) FROM dual
2055 union all
2056 SELECT DBMS_METADATA.GET_DEPENDENT_DDL('TRIGGER', ?, ?) FROM dual
2058 query($sqlstr, 'quiet-list', {num_rows => $num_rows, op => $op, op_text => $op_text}
2059 ,uc($object_name)
2060 ,uc($dbuser)
2061 ,uc($object_name)
2062 ,uc($dbuser)
2063 ,uc($object_name)
2064 ,uc($dbuser)
2065 ,uc($object_name)
2066 ,uc($dbuser)
2068 }elsif (
2069 $object_type eq 'SYNONYM'
2070 or $object_type eq 'VIEW'
2071 or $object_type eq 'TRIGGER'
2072 or $object_type eq 'SEQUENCE'
2073 or $object_type eq 'INDEX'
2074 or $object_type eq 'PROCEDURE'
2075 or $object_type eq 'FUNCTION'
2077 my $sqlstr = q{
2078 SELECT DBMS_METADATA.GET_DDL(?, ?, ?) FROM dual
2080 query($sqlstr, 'quiet-list', {num_rows => $num_rows, op => $op, op_text => $op_text}
2081 ,uc($object_type)
2082 ,uc($object_name)
2083 ,uc($dbuser)
2085 }else{
2086 query_err("show dll", "Unsupported object type ($object_name is a $object_type)", $input);
2089 $dbh->{LongReadLen} = $prev_LongReadLen;
2090 } else {
2091 query_err("show", "Unsupported show type", $input);
2093 } elsif($input =~ /^\s*show\s+invalid\s+objects\s*$/) {
2095 my $sqlstr = q{
2096 select
2097 object_name "Object Name",
2098 object_type "Type",
2099 status "Status"
2100 from user_objects WHERE status = 'INVALID'
2102 query($sqlstr, 'table', {num_rows => $num_rows, op => $op, op_text => $op_text});
2104 } elsif($input =~ /^\s*show\s+all\s+([a-zA-Z0-9_\$\#]+)\s*([a-zA-Z0-9_\'\$\#\%\s]*)$/) {
2105 my $object = $1;
2106 my $rest = $2;
2107 my $option = '';
2108 my $option_value = '';
2109 my $opts = {
2110 num_rows => $num_rows
2111 ,op => $op
2112 ,op_text => $op_text
2114 # Workaround for materialized views
2115 if ($object eq 'materialized' and $2 =~ /views\s*([a-zA-Z0-9_\$\#\%\s]*)/ ){
2116 $object = 'materialized views';
2117 $rest = $1;
2120 if ($rest =~ /\s*(\w+)\s+[']?([a-zA-Z0-9_\$\#\%]+)[']?/){
2121 $option = lc($1);
2122 $option_value = uc($2);
2125 if($object eq 'tables') {
2127 show_all_query(
2128 q{select table_name "Table Name", 'TABLE' "Type", owner "Owner" from all_tables }
2129 ,q{ order by table_name }
2130 ,$format
2131 ,$opts
2132 ,q{}
2133 ,$option
2134 ,q{table_name}
2135 ,$option_value
2138 } elsif($object eq 'views') {
2140 show_all_query(
2141 q{select view_name "View Name", 'VIEW' "Type", owner "Owner" from all_views }
2142 ,q{ order by view_name }
2143 ,$format
2144 ,$opts
2145 ,q{}
2146 ,$option
2147 ,q{view_name}
2148 ,$option_value
2151 } elsif($object eq 'objects') {
2153 show_all_query(
2154 q{select object_name "Object Name", object_type "Type", owner "Owner" from all_objects }
2155 ,q{ order by object_name }
2156 ,$format
2157 ,$opts
2158 ,q{}
2159 ,$option
2160 ,q{object_name}
2161 ,$option_value
2164 } elsif($object eq 'sequences') {
2166 show_all_query(
2167 q{select sequence_name "Sequence Name", 'SEQUENCE' "Type", sequence_owner "Owner" from all_sequences }
2168 ,q{ order by sequence_name }
2169 ,$format
2170 ,$opts
2171 ,q{}
2172 ,$option
2173 ,q{sequence_name}
2174 ,$option_value
2177 } elsif($object eq 'clusters') {
2179 show_all_query(
2180 q{select cluster_name "Cluster Name", 'CLUSTER' "Type", owner "Owner" from all_clusters}
2181 ,q{ order by cluster_name }
2182 ,$format
2183 ,$opts
2184 ,q{}
2185 ,$option
2186 ,q{cluster_name}
2187 ,$option_value
2190 } elsif($object eq 'dimensions') {
2192 show_all_query(
2193 q{select dimension_name "Dimension Name", 'DIMENSION' "Type", owner "Owner" from all_dimensions}
2194 ,q{ order by dimension_name }
2195 ,$format
2196 ,$opts
2197 ,q{}
2198 ,$option
2199 ,q{dimension_name}
2200 ,$option_value
2203 } elsif($object eq 'functions') {
2205 show_all_query(
2206 q{select distinct name "Function Name", 'FUNCTION' "Type", owner "Owner" from all_source}
2207 ,q{ order by name }
2208 ,$format
2209 ,$opts
2210 ,q{type = 'FUNCTION'}
2211 ,$option
2212 ,q{name}
2213 ,$option_value
2216 } elsif($object eq 'procedures') {
2218 show_all_query(
2219 q{select distinct name "Procedure Name", 'PROCEDURE' "Type", owner "Owner" from all_source}
2220 ,q{ order by name }
2221 ,$format
2222 ,$opts
2223 ,q{type = 'PROCEDURE'}
2224 ,$option
2225 ,q{name}
2226 ,$option_value
2229 } elsif($object eq 'packages') {
2231 show_all_query(
2232 q{select distinct name "Package Name", 'PACKAGES' "Type", owner "Owner" from all_source}
2233 ,q{ order by name }
2234 ,$format
2235 ,$opts
2236 ,q{type = 'PACKAGE'}
2237 ,$option
2238 ,q{name}
2239 ,$option_value
2242 } elsif($object eq 'indexes') {
2244 show_all_query(
2245 q{select index_name "Index Name", 'INDEXES' "Type", owner "Owner" from all_indexes}
2246 ,q{ order by index_name }
2247 ,$format
2248 ,$opts
2249 ,q{}
2250 ,$option
2251 ,q{index_name}
2252 ,$option_value
2255 } elsif($object eq 'indextypes') {
2257 show_all_query(
2258 q{select indextype_name "Indextype Name", 'INDEXTYPE' "Type", owner "Owner" from all_indextypes}
2259 ,q{ order by indextype_name }
2260 ,$format
2261 ,$opts
2262 ,q{}
2263 ,$option
2264 ,q{indextype_name}
2265 ,$option_value
2268 } elsif($object eq 'libraries') {
2270 show_all_query(
2271 q{select library_name "library Name", 'LIBRARY' "Type", owner "Owner" from all_libraries}
2272 ,q{ order by library_name }
2273 ,$format
2274 ,$opts
2275 ,q{}
2276 ,$option
2277 ,q{library_name}
2278 ,$option_value
2281 } elsif($object eq 'materialized views') {
2283 show_all_query(
2284 q{select mview_name "Materialized View Name", 'MATERIALIZED VIEW' "Type", owner "Owner" from all_mviews}
2285 ,q{ order by mview_name }
2286 ,$format
2287 ,$opts
2288 ,q{}
2289 ,$option
2290 ,q{mview_name}
2291 ,$option_value
2294 } elsif($object eq 'snapshots') {
2296 show_all_query(
2297 q{select name "Snapshot Name", 'SNAPSHOT' "Type", owner "Owner" from all_snapshots}
2298 ,q{ order by name }
2299 ,$format
2300 ,$opts
2301 ,q{}
2302 ,$option
2303 ,q{name}
2304 ,$option_value
2307 } elsif($object eq 'synonyms') {
2309 show_all_query(
2310 q{select
2311 synonym_name "Synonym Name",
2312 'SYNONYM' "Type",
2313 owner "Owner",
2314 TABLE_OWNER "Table Owner",
2315 TABLE_NAME "Table Name",
2316 DB_LINK "DB Link"
2317 from all_synonyms}
2318 ,q{ order by synonym_name }
2319 ,$format
2320 ,$opts
2321 ,q{}
2322 ,$option
2323 ,q{synonym_name}
2324 ,$option_value
2328 } elsif($object eq 'triggers') {
2330 show_all_query(
2331 q{select trigger_name "Trigger Name", 'TRIGGER' "Type", owner "Owner" from all_triggers}
2332 ,q{ order by trigger_name }
2333 ,$format
2334 ,$opts
2335 ,q{}
2336 ,$option
2337 ,q{trigger_name}
2338 ,$option_value
2341 } elsif($object eq 'waits') {
2342 my $sqlstr = q{
2343 select vs.username "Username",
2344 vs.osuser "OS User",
2345 vsw.sid "SID",
2346 vsw.event "Event",
2347 decode(vsw.wait_time, -2, ' Unknown',
2348 to_char(vsw.seconds_in_wait,'999,999,999,999'))
2349 "Seconds Waiting"
2350 from v$session_wait vsw,
2351 v$session vs
2352 where vsw.sid = vs.sid
2353 order by vsw.wait_time desc, vsw.seconds_in_wait desc, vsw.sid
2355 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2356 op_text => $op_text});
2358 } elsif( $object eq 'constraints' ){
2360 my $sqlstr = q{
2361 select
2362 CONSTRAINT_NAME "Constraint Name"
2363 ,decode(constraint_type,
2364 'C', 'Check',
2365 'P', 'Primary Key',
2366 'R', 'Foreign Key',
2367 'U', 'Unique',
2368 '') "Type"
2369 ,TABLE_NAME "Table Name"
2370 ,INDEX_NAME "Index Name"
2371 ,STATUS "Status"
2372 from all_constraints
2374 show_all_query(
2375 $sqlstr
2376 ,q{ order by CONSTRAINT_NAME }
2377 ,$format
2378 ,$opts
2379 ,q{}
2380 ,$option
2381 ,q{CONSTRAINT_NAME}
2382 ,$option_value
2385 } else {
2386 query_err("show", "Unsupported show type", $input);
2388 } elsif($input =~ /^\s*show\s+([a-zA-Z0-9_\$\#\s]+)\s*$/) {
2389 if($1 eq 'tables') {
2390 my $sqlstr = q{
2391 select table_name "Table Name", 'TABLE' "Type", sys.login_user() "Owner"
2392 from user_tables
2393 order by table_name
2395 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2396 op_text => $op_text});
2397 } elsif($1 eq 'views') {
2398 my $sqlstr = q{
2399 select view_name "View Name", 'VIEW' "Type", sys.login_user() "Owner"
2400 from user_views
2401 order by view_name
2403 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2404 op_text => $op_text});
2405 } elsif($1 eq 'objects') {
2406 my $sqlstr = q{
2407 select object_name "Object Name", object_type "Type", sys.login_user() "Owner"
2408 from user_objects
2409 order by object_name
2411 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2412 op_text => $op_text});
2413 } elsif($1 eq 'sequences') {
2414 my $sqlstr = q{
2415 select sequence_name "Sequence Name", 'SEQUENCE' "Type", sys.login_user() "Owner"
2416 from user_sequences
2417 order by sequence_name
2419 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2420 op_text => $op_text});
2421 } elsif($1 eq 'clusters') {
2422 my $sqlstr = q{
2423 select cluster_name "Cluster Name", 'CLUSTER' "Type", sys.login_user() "Owner"
2424 from user_clusters
2425 order by cluster_name
2427 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2428 op_text => $op_text});
2429 } elsif($1 eq 'dimensions') {
2430 my $sqlstr = q{
2431 select dimension_name "Dimension Name", 'DIMENSION' "Type", sys.login_user() "Owner"
2432 from user_dimensions
2433 order by dimension_name
2435 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2436 op_text => $op_text});
2437 } elsif($1 eq 'functions') {
2438 my $sqlstr = q{
2439 select distinct name "Function Name", 'FUNCTION' "Type", sys.login_user() "Owner"
2440 from user_source
2441 where type = 'FUNCTION'
2442 order by name
2444 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2445 op_text => $op_text});
2446 } elsif($1 eq 'procedures') {
2447 my $sqlstr = q{
2448 select distinct name "Procedure Name", 'PROCEDURE' "Type", sys.login_user() "Owner"
2449 from user_source
2450 where type = 'PROCEDURE'
2451 order by name
2453 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2454 op_text => $op_text});
2455 } elsif($1 eq 'packages') {
2456 my $sqlstr = q{
2457 select distinct name "Package Name", 'PACKAGES' "Type", sys.login_user() "Owner"
2458 from user_source
2459 where type = 'PACKAGE'
2460 order by name
2462 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2463 op_text => $op_text});
2464 } elsif($1 eq 'indexes') {
2465 my $sqlstr = q{
2466 select index_name "Index Name", 'INDEXES' "Type", sys.login_user() "Owner"
2467 from user_indexes
2468 order by index_name
2470 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2471 op_text => $op_text});
2472 } elsif($1 eq 'indextypes') {
2473 my $sqlstr = q{
2474 select indextype_name "Indextype Name", 'INDEXTYPE' "Type", sys.login_user() "Owner"
2475 from user_indextypes
2476 order by indextype_name
2478 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2479 op_text => $op_text});
2480 } elsif($1 eq 'libraries') {
2481 my $sqlstr = q{
2482 select library_name "library Name", 'LIBRARY' "Type", sys.login_user() "Owner"
2483 from user_libraries
2484 order by library_name
2486 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2487 op_text => $op_text});
2488 } elsif($1 eq 'materialized views') {
2489 my $sqlstr = q{
2490 select mview_name "Materialized View Name", 'MATERIALIZED VIEW' "Type", sys.login_user() "Owner"
2491 from user_mviews
2492 order by mview_name
2494 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2495 op_text => $op_text});
2496 } elsif($1 eq 'snapshots') {
2497 my $sqlstr = q{
2498 select name "Snapshot Name", 'SNAPSHOT' "Type", sys.login_user() "Owner"
2499 from user_snapshots
2500 order by name
2502 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2503 op_text => $op_text});
2504 } elsif($1 eq 'synonyms') {
2505 my $sqlstr = q{
2506 select
2507 synonym_name "Synonym Name",
2508 'SYNONYM' "Type",
2509 sys.login_user() "Owner",
2510 TABLE_OWNER "Table Owner",
2511 TABLE_NAME "Table Name",
2512 DB_LINK "DB Link"
2513 from user_synonyms
2514 order by synonym_name
2516 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2517 op_text => $op_text});
2518 } elsif($1 eq 'triggers') {
2519 my $sqlstr = q{
2520 select trigger_name "Trigger Name", 'TRIGGER' "Type", sys.login_user() "Owner"
2521 from user_triggers
2522 order by trigger_name
2524 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2525 op_text => $op_text});
2526 } elsif($1 eq 'processes') {
2527 my $sqlstr = q{
2528 select sid,
2529 vs.username "User",
2530 vs.status "Status",
2531 vs.schemaname "Schema",
2532 vs.osuser || '@' || vs.machine "From",
2533 to_char(vs.logon_time, 'Mon DD YYYY HH:MI:SS') "Logon Time",
2534 aa.name "Command"
2535 from v$session vs, audit_actions aa
2536 where vs.command = aa.action
2537 and username is not null
2539 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2540 op_text => $op_text});
2541 } elsif($1 eq 'locks') {
2542 my $sqlstr = q{
2543 SELECT
2544 s.username "Username"
2545 ,s.osuser || '@' || s.MACHINE "User@Machine"
2546 ,s.PROGRAM "Program"
2547 ,s.sid sid
2548 ,l.LMODE || ':' ||
2549 decode(L.LMODE,
2550 1,'No Lock',
2551 2,'Row Share',
2552 3,'Row Exclusive',
2553 4,'Share',
2554 5,'Share Row Exclusive',
2555 6,'Exclusive','NONE') "LMode"
2556 ,l.type || ':' ||
2557 decode(l.type,
2558 'BL','Buffer hash table instance lock',
2559 'CF',' Control file schema global enqueue lock',
2560 'CI','Cross-instance function invocation instance lock',
2561 'CS','Control file schema global enqueue lock',
2562 'CU','Cursor bind lock',
2563 'DF','Data file instance lock',
2564 'DL','Direct loader parallel index create',
2565 'DM','Mount/startup db primary/secondary instance lock',
2566 'DR','Distributed recovery process lock',
2567 'DX','Distributed transaction entry lock',
2568 'FI','SGA open-file information lock',
2569 'FS','File set lock',
2570 'HW','Space management operations on a specific segment lock',
2571 'IN','Instance number lock',
2572 'IR','Instance recovery serialization global enqueue lock',
2573 'IS','Instance state lock',
2574 'IV','Library cache invalidation instance lock',
2575 'JQ','Job queue lock',
2576 'KK','Thread kick lock',
2577 'MB','Master buffer hash table instance lock',
2578 'MM','Mount definition gloabal enqueue lock',
2579 'MR','Media recovery lock',
2580 'PF','Password file lock',
2581 'PI','Parallel operation lock',
2582 'PR','Process startup lock',
2583 'PS','Parallel operation lock',
2584 'RE','USE_ROW_ENQUEUE enforcement lock',
2585 'RT','Redo thread global enqueue lock',
2586 'RW','Row wait enqueue lock',
2587 'SC','System commit number instance lock',
2588 'SH','System commit number high water mark enqueue lock',
2589 'SM','SMON lock',
2590 'SN','Sequence number instance lock',
2591 'SQ','Sequence number enqueue lock',
2592 'SS','Sort segment lock',
2593 'ST','Space transaction enqueue lock',
2594 'SV','Sequence number value lock',
2595 'TA','Generic enqueue lock',
2596 'TD','DDL enqueue lock',
2597 'TE','Extend-segment enqueue lock',
2598 'TM','DML enqueue lock',
2599 'TT','Temporary table enqueue lock',
2600 'TX','Transaction enqueue lock',
2601 'UL','User supplied lock',
2602 'UN','User name lock',
2603 'US','Undo segment DDL lock',
2604 'WL','Being-written redo log instance lock',
2605 'WS','Write-atomic-log-switch global enqueue lock') "Lock Type"
2606 ,CASE
2607 WHEN l.type = 'TM' THEN (
2608 SELECT OBJECT_TYPE || ' : ' || OWNER || '.' || OBJECT_NAME
2609 FROM ALL_OBJECTS
2610 where object_id = l.id1
2612 WHEN l.type = 'TX' AND l.BLOCK = 1 THEN (
2613 SELECT
2614 'Blocked Sessions: ' || max(substr(SYS_CONNECT_BY_PATH(SID, ','),2)) SID
2615 FROM (
2616 SELECT
2617 l2.id1,
2618 l2.id2,
2619 l2.SID,
2620 row_number() OVER (Partition by l2.id1 order by l2.id1 ) seq
2621 FROM
2622 v$lock l2
2623 WHERE
2624 l2.block = 0
2626 where id1 = l.id1
2627 and id2 = l.id2
2628 start with
2629 seq=1
2630 connect by prior
2631 seq+1=seq
2632 and prior
2633 id1=id1
2634 GROUP BY id1
2636 WHEN l.type = 'TX' AND l.REQUEST > 0 THEN (
2637 SELECT
2638 'Wait for Session: ' || SID
2639 FROM V$LOCK l2
2640 WHERE l.id1 = l2.id1
2641 and l.id2 = l2.id2
2642 and block = 1
2644 ELSE 'unknown'
2645 END AS "Locked object / Lock Info"
2646 ,l.CTIME
2647 FROM V$LOCK l
2648 LEFT JOIN V$SESSION s ON l.SID = s.SID
2649 WHERE l.type <> 'MR' AND s.type <> 'BACKGROUND'
2651 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2652 op_text => $op_text});
2654 } elsif($1 eq 'waits') {
2655 my $sqlstr = q{
2656 select vs.username "Username",
2657 vs.osuser "OS User",
2658 vsw.sid "SID",
2659 vsw.event "Event",
2660 decode(vsw.wait_time, -2, ' Unknown',
2661 to_char(vsw.seconds_in_wait,'999,999,999,999'))
2662 "Seconds Waiting"
2663 from v$session_wait vsw,
2664 v$session vs
2665 where vsw.sid = vs.sid
2666 and vs.status = 'ACTIVE'
2667 and vs.username is not null
2668 order by vsw.wait_time desc, vsw.seconds_in_wait desc, vsw.sid
2670 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2671 op_text => $op_text});
2672 } elsif($1 eq 'plan') {
2673 # This following query is Copyright (c) Oracle Corporation 1998, 1999. All Rights Reserved.
2674 my $sqlstr = q{
2675 select '| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |' as "Plan Table" from dual
2676 union all
2677 select '--------------------------------------------------------------------------------' from dual
2678 union all
2679 select rpad('| '||substr(lpad(' ',1*(level-1)) ||operation||
2680 decode(options, null,'',' '||options), 1, 27), 28, ' ')||'|'||
2681 rpad(substr(object_name||' ',1, 9), 10, ' ')||'|'||
2682 lpad(decode(cardinality,null,' ',
2683 decode(sign(cardinality-1000), -1, cardinality||' ',
2684 decode(sign(cardinality-1000000), -1, trunc(cardinality/1000)||'K',
2685 decode(sign(cardinality-1000000000), -1, trunc(cardinality/1000000)||'M',
2686 trunc(cardinality/1000000000)||'G')))), 7, ' ') || '|' ||
2687 lpad(decode(bytes,null,' ',
2688 decode(sign(bytes-1024), -1, bytes||' ',
2689 decode(sign(bytes-1048576), -1, trunc(bytes/1024)||'K',
2690 decode(sign(bytes-1073741824), -1, trunc(bytes/1048576)||'M',
2691 trunc(bytes/1073741824)||'G')))), 6, ' ') || '|' ||
2692 lpad(decode(cost,null,' ',
2693 decode(sign(cost-10000000), -1, cost||' ',
2694 decode(sign(cost-1000000000), -1, trunc(cost/1000000)||'M',
2695 trunc(cost/1000000000)||'G'))), 8, ' ') || '|' ||
2696 lpad(decode(partition_start, 'ROW LOCATION', 'ROWID',
2697 decode(partition_start, 'KEY', 'KEY', decode(partition_start,
2698 'KEY(INLIST)', 'KEY(I)', decode(substr(partition_start, 1, 6),
2699 'NUMBER', substr(substr(partition_start, 8, 10), 1,
2700 length(substr(partition_start, 8, 10))-1),
2701 decode(partition_start,null,' ',partition_start)))))||' ', 7, ' ')|| '|' ||
2702 lpad(decode(partition_stop, 'ROW LOCATION', 'ROW L',
2703 decode(partition_stop, 'KEY', 'KEY', decode(partition_stop,
2704 'KEY(INLIST)', 'KEY(I)', decode(substr(partition_stop, 1, 6),
2705 'NUMBER', substr(substr(partition_stop, 8, 10), 1,
2706 length(substr(partition_stop, 8, 10))-1),
2707 decode(partition_stop,null,' ',partition_stop)))))||' ', 7, ' ')||'|' as "Explain plan"
2708 from plan_table
2709 start with id=0 and timestamp = (select max(timestamp) from plan_table where id=0)
2710 connect by prior id = parent_id
2711 and prior nvl(statement_id, ' ') = nvl(statement_id, ' ')
2712 and prior timestamp <= timestamp
2713 union all
2714 select '--------------------------------------------------------------------------------' from dual
2716 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2717 op_text => $op_text});
2718 } elsif($1 eq 'errors') {
2719 my $err = $dbh->func( 'plsql_errstr' );
2720 if($err) {
2721 print "\n$err\n\n";
2722 } else {
2723 print "\nNo errors.\n\n";
2725 } elsif($1 eq 'users') {
2726 my $sqlstr = q{
2727 select username, user_id, created
2728 from all_users
2730 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2731 op_text => $op_text});
2732 } elsif($1 eq 'user') {
2733 my $sqlstr = q{
2734 select user from dual
2736 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2737 op_text => $op_text});
2738 } elsif($1 eq 'uid') {
2739 my $sqlstr = q{
2740 select uid from dual
2742 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2743 op_text => $op_text});
2744 } elsif(($1 eq 'database links') || ($1 eq 'dblinks')) {
2745 my $sqlstr = q{
2746 select db_link, host, owner from all_db_links
2748 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2749 op_text => $op_text});
2751 } elsif($1 eq 'recyclebin') {
2752 my $sqlstr = q{
2753 select
2754 original_name "Original Name",
2755 object_name "Object Name",
2756 type "Object type",
2757 droptime "Droptime"
2758 from recyclebin
2759 order by 4
2761 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2762 op_text => $op_text});
2763 } else {
2764 query_err("show", "Unsupported show type", $input);
2766 } else {
2767 query_err("show", "Unsupported show type", $input);
2772 sub describe {
2773 my($input, $format, $nosynonym, $num_rows, $op, $op_text) = @_;
2774 debugmsg(3, "describe called", @_);
2775 # This describes a table, view, sequence, or synonym by listing it's
2776 # columns and their attributes
2778 # convert to lowercase for comparison operations
2779 $input = lc($input);
2781 # make sure we're still connected to the database
2782 unless(ping()) {
2783 wrn("Database connection died");
2784 db_reconnect();
2787 # parse the query to find the table that was requested to be described
2788 if($input =~ /^\s*desc\w*\s*([a-zA-Z0-9_\$\#\.\@]+)/) {
2789 my $object = $1;
2790 my $sqlstr;
2791 my $type;
2792 my @ret;
2794 my $schema;
2795 my $dblink;
2796 if($object =~ /^([a-zA-Z0-9_\$\#]+)\.([a-zA-Z0-9_\$\#]+)\@([a-zA-Z0-9_\$\#]+)$/) {
2797 $schema = $1;
2798 $object = $2;
2799 $dblink = "\@$3";
2800 } elsif($object =~ /^([a-zA-Z0-9_\$\#]+)\@([a-zA-Z0-9_\$\#]+)$/) {
2801 $schema = $dbuser;
2802 $object = $1;
2803 $dblink = "\@$2";
2804 } elsif($object =~ /^([a-zA-Z0-9_\$\#]+)\.([a-zA-Z0-9_\$\#]+)$/) {
2805 $schema = $1;
2806 $object = $2;
2807 } else {
2808 $schema = $dbuser;
2811 debugmsg(1,"schema: [$schema] object: [$object] dblink: [$dblink]");
2813 if($conf{fast_describe}) {
2814 if(my $sth = $dbh->prepare("select * from $schema.$object$dblink")) {
2815 my $fields = $sth->{NAME};
2816 my $types = $sth->{TYPE};
2817 my $type_info = $dbh->type_info($types->[0]);
2818 my $precision = $sth->{PRECISION};
2819 my $scale = $sth->{SCALE};
2820 my $nullable = $sth->{NULLABLE};
2822 debugmsg(4, "fields: [" . join(',', @$fields) . "]");
2823 debugmsg(4, "types: [" . join(',', @$types) . "]");
2824 debugmsg(4, "type_info: [" . Dumper($type_info) . "]");
2825 debugmsg(4, "precision: [" . join(',', @$precision) . "]");
2826 debugmsg(4, "scale: [" . join(',', @$scale) . "]");
2827 debugmsg(4, "nullable: [" . join(',', @$nullable) . "]");
2829 # Assemble a multidiminsional array of the output
2830 my @desc;
2831 for(my $i = 0; $i < @$fields; $i++) {
2832 my ($name, $null, $type);
2833 $name = $fields->[$i];
2834 $null = ($nullable->[$i] ? 'NULL' : 'NOT NULL');
2835 my $type_info = $dbh->type_info($types->[$i]);
2836 $type = $type_info->{'TYPE_NAME'};
2837 # convert DECIMAL to NUMBER for our purposes (some kind of DBD kludge)
2838 $type = 'NUMBER' if $type eq 'DECIMAL';
2839 if( $type eq 'VARCHAR2' || $type eq 'NVARCHAR2' ||
2840 $type eq 'CHAR' || $type eq 'NCHAR' || $type eq 'RAW' )
2842 $type .= "($precision->[$i])";
2843 } elsif($type eq 'NUMBER' && ($scale->[$i] || $precision->[$i] < 38))
2845 $type .= "($precision->[$i],$scale->[$i])";
2847 push(@desc, [$name, $null, $type]);
2850 # figure max column sizes we'll need
2851 my @widths = (4,5,4);
2852 for(my $i = 0; $i < @desc; $i++) {
2853 for(my $j = 0; $j < @{$desc[0]}; $j++) {
2854 if(length($desc[$i][$j]) > $widths[$j]) {
2855 $widths[$j] = length($desc[$i][$j]);
2860 # open the redirection file
2861 if($op && $op eq '>' || $op eq '>>') {
2862 ($op_text) = glob($op_text);
2863 debugmsg(3, "Opening file '$op_text' for output redirection using [$op]");
2864 open(FOUT, $op . $op_text) || do query_err('redirect',"Cannot open file '$op_text' for writing: $!", '');
2865 } elsif($op eq '|') {
2866 debugmsg(3, "Opening pipe to '$op_text' for output redirection");
2867 open(FOUT, $op . $op_text) || do query_err('pipe',"Cannot open pipe '$op_text': $!", '');
2868 } else {
2869 open(FOUT, ">&STDOUT");
2872 if($opt_headers) {
2873 # Print headers
2874 print FOUT "\n";
2875 print FOUT sprintf("%-$widths[0]s", 'Name')
2876 . ' '
2877 . sprintf("%-$widths[1]s", 'Null?')
2878 . ' '
2879 . sprintf("%-$widths[2]s", 'Type')
2880 . "\n";
2881 print FOUT '-' x $widths[0]
2882 . ' '
2883 . '-' x $widths[1]
2884 . ' '
2885 . '-' x $widths[2]
2886 . "\n";
2888 for(my $i = 0; $i < @desc; $i++) {
2889 for(my $j = 0; $j < @{$desc[$i]}; $j++) {
2890 print FOUT ' ' if $j > 0;
2891 print FOUT sprintf("%-$widths[$j]s", $desc[$i][$j]);
2893 print FOUT "\n";
2895 print FOUT "\n";
2897 close(FOUT);
2899 return();
2903 # look in all_constraints for the object first. This is because oracle
2904 # stores information about primary keys in the all_objects table as "index"s
2905 # but it doesn't have foreign keys or constraints. So we want to match
2906 # there here first
2908 # now look in all_objects
2909 my $all_object_cols = 'object_type,owner,object_name,'
2910 . 'object_id,created,last_ddl_time,'
2911 . 'timestamp,status';
2913 @ret = $dbh->selectrow_array(
2914 "select $all_object_cols from all_objects where object_name = ? "
2915 ."and owner = ?"
2916 .($nosynonym ? " and object_type != 'SYNONYM'" : ""),
2917 undef, uc($object), uc($schema)
2918 ) or
2919 @ret = $dbh->selectrow_array(
2920 "select $all_object_cols from all_objects where object_name = ? "
2921 ."and owner = 'PUBLIC'"
2922 .($nosynonym ? " and object_type != 'SYNONYM'" : ""),
2923 undef, uc($object)
2926 unless(@ret) {
2927 @ret = $dbh->selectrow_array(
2928 "select constraint_type, constraint_name from all_constraints where "
2929 ."constraint_name = ?",
2930 undef, uc($object)
2934 if($ret[0] eq 'INDEX') {
2935 # Check if this 'index' is really a primary key and is in the
2936 # all_constraints table
2938 my @temp_ret = $dbh->selectrow_array(
2939 "select constraint_type, constraint_name from all_constraints where "
2940 ."constraint_name = ?",
2941 undef, uc($object)
2944 @ret = @temp_ret if @temp_ret;
2947 $type = $ret[0];
2948 debugmsg(1,"type: [$type] ret: [@ret]");
2950 if($type eq 'SYNONYM') {
2951 # Find what this is a synonym to, then recursively call this function
2952 # again to describe whatever it points to
2953 my($table_name, $table_owner) = $dbh->selectrow_array(
2954 'select table_name, table_owner from all_synonyms '
2955 .'where synonym_name = ? and owner = ?',
2956 undef, uc($ret[2]), uc($ret[1])
2959 describe("desc $table_owner.$table_name", $format, 1);
2960 } elsif($type eq 'SEQUENCE') {
2961 my $sqlstr = q{
2962 select sequence_name "Name",
2963 min_value "Min",
2964 max_value "Max",
2965 increment_by "Inc",
2966 cycle_flag "Cycle",
2967 order_flag "Order",
2968 last_number "Last"
2969 from all_sequences
2970 where sequence_name = ?
2971 and sequence_owner = ?
2973 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2974 op_text => $op_text}, uc($ret[2]), uc($ret[1]));
2975 } elsif($type eq 'TABLE' || $type eq 'VIEW' || $type eq 'TABLE PARTITION') {
2976 my $sqlstr = q{
2977 select column_name "Name",
2978 decode(nullable,
2979 'N','NOT NULL'
2980 ) "Null?",
2981 decode(data_type,
2982 'VARCHAR2','VARCHAR2(' || TO_CHAR(data_length) || ')',
2983 'NVARCHAR2','NVARCHAR2(' || TO_CHAR(data_length) || ')',
2984 'CHAR','CHAR(' || TO_CHAR(data_length) || ')',
2985 'NCHAR','NCHAR(' || TO_CHAR(data_length) || ')',
2986 'NUMBER',
2987 decode(data_precision,
2988 NULL, 'NUMBER',
2989 'NUMBER(' || TO_CHAR(data_precision)
2990 || ',' || TO_CHAR(data_scale) || ')'
2992 'FLOAT',
2993 decode(data_precision,
2994 NULL, 'FLOAT', 'FLOAT(' || TO_CHAR(data_precision) || ')'
2996 'DATE','DATE',
2997 'LONG','LONG',
2998 'LONG RAW','LONG RAW',
2999 'RAW','RAW(' || TO_CHAR(data_length) || ')',
3000 'MLSLABEL','MLSLABEL',
3001 'ROWID','ROWID',
3002 'CLOB','CLOB',
3003 'NCLOB','NCLOB',
3004 'BLOB','BLOB',
3005 'BFILE','BFILE',
3006 data_type || ' ???'
3007 ) "Type",
3008 data_default "Default"
3009 from all_tab_columns atc
3010 where table_name = ?
3011 and owner = ?
3012 order by column_id
3014 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
3015 op_text => $op_text}, uc($ret[2]), uc($ret[1]));
3016 } elsif($type eq 'R') {
3017 my $sqlstr = q{
3018 select ac.constraint_name "Name",
3019 decode(ac.constraint_type,
3020 'R', 'Foreign Key',
3021 'C', 'Check',
3022 'U', 'Unique',
3023 'P', 'Primary Key',
3024 ac.constraint_type) "Type",
3025 ac.table_name "Table Name",
3026 acc.column_name "Column Name",
3027 r_ac.table_name "Parent Table",
3028 r_acc.column_name "Parent Column",
3029 ac.delete_rule "Delete Rule"
3030 from all_constraints ac, all_cons_columns acc,
3031 all_constraints r_ac, all_cons_columns r_acc
3032 where ac.constraint_name = acc.constraint_name
3033 and ac.owner = acc.owner
3034 and ac.r_constraint_name = r_ac.constraint_name
3035 and r_ac.constraint_name = r_acc.constraint_name
3036 and r_ac.owner = r_acc.owner
3037 and ac.constraint_type = 'R'
3038 and ac.constraint_name = ?
3039 and ac.owner = ?
3040 order by ac.constraint_name, acc.position
3042 query($sqlstr, 'list_aligned', {num_rows => $num_rows, op => $op,
3043 op_text => $op_text}, uc($ret[1]),
3044 uc($schema));
3045 } elsif($type eq 'P' || $type eq 'U') {
3046 my $sqlstr = q{
3047 select ac.constraint_name "Name",
3048 decode(ac.constraint_type,
3049 'R', 'Foreign Key',
3050 'C', 'Check',
3051 'U', 'Unique',
3052 'P', 'Primary Key',
3053 ac.constraint_type) "Type",
3054 ac.table_name "Table Name",
3055 acc.column_name "Column Name"
3056 from all_constraints ac, all_cons_columns acc
3057 where ac.constraint_name = acc.constraint_name
3058 and ac.owner = acc.owner
3059 and ac.constraint_name = ?
3060 and ac.owner = ?
3061 order by ac.constraint_name, acc.position
3063 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
3064 op_text => $op_text}, uc($ret[1]), uc($schema));
3065 } elsif($type eq 'C') {
3066 my $sqlstr = q{
3067 select ac.constraint_name "Name",
3068 decode(ac.constraint_type,
3069 'R', 'Foreign Key',
3070 'C', 'Check',
3071 'U', 'Unique',
3072 'P', 'Primary Key',
3073 ac.constraint_type) "Type",
3074 ac.table_name "Table Name",
3075 ac.search_condition "Search Condition"
3076 from all_constraints ac
3077 where ac.constraint_name = ?
3078 order by ac.constraint_name
3080 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
3081 op_text => $op_text}, uc($ret[1]));
3082 } elsif($type eq 'INDEX') {
3083 my $sqlstr = q{
3084 select ai.index_name "Index Name",
3085 ai.index_type "Type",
3086 ai.table_name "Table Name",
3087 ai.uniqueness "Unique?",
3088 aic.column_name "Column Name"
3089 from all_indexes ai, all_ind_columns aic
3090 where ai.index_name = aic.index_name(+)
3091 and ai.table_owner = aic.table_owner(+)
3092 and ai.index_name = ?
3093 and ai.table_owner = ?
3094 order by aic.column_position
3096 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
3097 op_text => $op_text}, uc($ret[2]), uc($schema));
3098 } elsif($type eq 'TRIGGER') {
3099 my $sqlstr = q{
3100 select trigger_name "Trigger Name",
3101 trigger_type "Type",
3102 triggering_event "Event",
3103 table_name "Table",
3104 when_clause "When",
3105 description "Description",
3106 trigger_body "Body"
3107 from all_triggers
3108 where trigger_name = ?
3110 query($sqlstr, 'list_aligned', {num_rows => $num_rows, op => $op,
3111 op_text => $op_text}, uc($ret[2]));
3112 } elsif($type eq 'PACKAGE') {
3113 wrn("Not implemented (yet)");
3114 } elsif($type eq 'PROCEDURE') {
3115 wrn("Not implemented (yet)");
3116 } elsif($type eq 'CLUSTER') {
3117 wrn("Not implemented (yet)");
3118 } elsif($type eq 'TRIGGER') {
3119 wrn("Not implemented (yet)");
3120 } else {
3121 query_err('describe', "Object $object not found");
3125 sub let_cmd {
3126 my($input) = @_;
3127 debugmsg(3, "let_cmd called", @_);
3128 my @bool_keys = qw(
3129 sql_query_in_error auto_complete edit_history fast_describe
3130 complete_objects complete_tables extended_complete_list
3131 extended_benchmarks column_wildcards complete_columns auto_commit
3132 commit_on_exit command_complete_list long_trunc_ok
3135 if ($input =~ /^\s*let\s*(\w+)?\s*/i ){
3136 my @print_keys = keys %conf;
3137 @print_keys = grep(/$1/,@print_keys) if ($1);
3139 foreach my $key ( @print_keys ){
3140 my $print_conf = $conf{$key};
3141 $print_conf = ($conf{$key}) ? 'On' : 'Off' if ( grep(/$key/,@bool_keys) );
3143 if ($key eq 'long_read_len' ){
3144 $print_conf = $dbh->{LongReadLen};
3147 printf("%25s : %1s\n",$key,$print_conf);
3149 }else{
3150 print "usage let <config name>\n";
3153 sub set_cmd {
3154 my($input) = @_;
3155 debugmsg(3, "set_cmd called", @_);
3156 # This mimics SQL*Plus set commands, or ignores them completely. For those
3157 # that are not supported, we do nothing at all, but return silently.
3159 if($input =~ /^\s*set\s+serverout(?:put)?\s+(on|off)(?:\s+size\s+(\d+))?/i) {
3160 if(lc($1) eq 'on') {
3161 my $size = $2 || 1_000_000;
3162 debugmsg(2, "calling dbms_output_enable($size)");
3163 $dbh->func( $size, 'dbms_output_enable' )
3164 or warn "dbms_output_enable($size) failed: $DBI::errstr\n";
3165 $set{serveroutput} = 1;
3166 debugmsg(2, "serveroutput set to $set{serveroutput}");
3167 } else {
3168 $set{serveroutput} = 0;
3169 debugmsg(2, "serveroutput set to $set{serveroutput}");
3171 }elsif($input =~ /^\s*set\s+(long_read_len|LongReadLen)\s+(\d+)/i){
3172 debugmsg(2, "long_read_len/LongReadLen set to $2");
3173 $dbh->{LongReadLen} = $2;
3174 $conf{long_read_len} = $2;
3175 }elsif($input =~ /^\s*set\s+fast_describe\s+(on|off)/i){
3176 $conf{fast_describe} = (lc($1) eq 'on') ? 1 : 0;
3177 print "fast_describe is now " . ($conf{fast_describe} ? 'on' : 'off') . "\n";
3179 }elsif($input =~ /^\s*set\s+(\w+)\s*/ ){
3180 print "Can't set option $1\n";
3184 sub query {
3185 my($sqlstr, $format, $opts, @bind_vars) = @_;
3186 debugmsg(3, "query called", @_);
3187 # this runs the provided query and calls format_display to display the results
3189 my $num_rows = $opts->{num_rows};
3190 my $op = $opts->{op};
3191 my $op_text = $opts->{op_text};
3192 my $result_output = ( exists $opts->{result_output}
3193 ? $opts->{result_output}
3197 my(@totalbench, @querybench, @formatbench);
3199 # Look for special query types, such as "show" and "desc" that we handle
3200 # and don't send to the database at all, since they're not really valid SQL.
3202 my ($rows_affected, $success_code);
3204 if($sqlstr =~ /^\s*desc/i) {
3205 describe($sqlstr, $format, undef, $num_rows, $op, $op_text);
3206 } elsif($sqlstr =~ /^\s*show/i) {
3207 show($sqlstr, $format, $num_rows, $op, $op_text);
3208 } else {
3209 $running_query = 1;
3211 # make sure we're still connected to the database
3212 unless(ping()) {
3213 wrn("Database connection died");
3214 db_reconnect();
3217 $sqlstr = wildcard_expand($sqlstr) if $conf{column_wildcards};
3219 # send the query on to the database
3220 push(@totalbench, get_bench()) if !$conf{extended_benchmarks};
3221 push(@querybench, get_bench()) if $conf{extended_benchmarks};
3222 debugmsg(3, "preparing", $sqlstr);
3223 my $sth = $dbh->prepare($sqlstr);
3224 unless($sth) {
3225 my $err = $DBI::errstr;
3226 $err =~ s/ \(DBD ERROR\: OCIStmtExecute\/Describe\)//;
3228 if ($err =~ m/DBD ERROR\:/) {
3229 my $indicator_offset = $DBI::errstr;
3230 $indicator_offset =~ s/(.*)(at\ char\ )(\d+)(\ .*)/$3/;
3231 if ($indicator_offset > 0) {
3232 my $i = 0;
3233 print $sqlstr, "\n";
3234 for ($i=0;$i<$indicator_offset;++$i) {
3235 print " ";
3237 print "*\n";
3241 # Output message if serveroutput is on
3242 if($set{serveroutput}) {
3243 debugmsg(3, "Calling dmbs_output_get");
3244 my @output = $dbh->func( 'dbms_output_get' );
3245 print join("\n", @output) . "\n";
3247 query_err('prepare', $err, $sqlstr), setup_sigs(), return();
3249 debugmsg(2, "sth: [$sth]");
3251 $cursth = $sth;
3253 finish_query($sth), return() if $sigintcaught; #pseudo sig handle
3255 my $ret;
3256 eval {
3257 debugmsg(3, "executing", $sqlstr);
3258 $ret = $sth->execute(@bind_vars);
3260 debugmsg(3, "ret:", $ret, "\@:", $@, "\$DBI::errstr:", $DBI::errstr);
3261 if(!$ret) {
3262 my $eval_error = $@;
3263 $eval_error =~ s/at \(eval \d+\) line \d+, <\S+> line \d+\.//;
3264 my $err = $DBI::errstr;
3265 $err =~ s/ \(DBD ERROR: OCIStmtExecute\)//;
3266 # Output message is serveroutput is on
3267 if($set{serveroutput}) {
3268 debugmsg(3, "Calling dmbs_output_get");
3269 my @output = $dbh->func( 'dbms_output_get' );
3270 print join("\n", @output) . "\n";
3272 my $errstr = ($eval_error ? $eval_error : $err);
3273 query_err('execute', $errstr, $sqlstr);
3274 setup_sigs();
3275 return();
3278 if($DBI::errstr =~ /^ORA-24344/) {
3279 print "\nWarning: Procedure created with compilation errors.\n\n";
3280 setup_sigs();
3281 return();
3284 push(@querybench, get_bench()) if $conf{extended_benchmarks};
3286 finish_query($sth), return() if $sigintcaught; #pseudo sig handle
3288 debugmsg(1, "rows returned: [" . $sth->rows() . "]");
3290 # open the redirection file
3291 if($op && $op eq '>' || $op eq '>>') {
3292 ($op_text) = glob($op_text);
3293 debugmsg(3, "Opening file '$op_text' for output redirection using [$op]");
3294 open(FOUT, $op . $op_text) || do{
3295 query_err('redirect',"Cannot open file '$op_text' for writing: $!",
3296 $sqlstr);
3297 finish_query($sth);
3298 return();
3300 } elsif($op eq '|') {
3301 debugmsg(3, "Opening pipe to '$op_text' for output redirection");
3302 open(FOUT, $op . $op_text) || do{
3303 query_err('pipe',"Cannot open pipe '$op_text': $!", $sqlstr);
3304 finish_query($sth);
3305 return();
3307 } else {
3308 open(FOUT, ">&STDOUT");
3311 # Output message is serveroutput is on
3312 if($set{serveroutput}) {
3313 debugmsg(3, "Calling dmbs_output_get");
3314 my @output = $dbh->func( 'dbms_output_get' );
3315 print join("\n", @output) . "\n";
3318 # Determine type and output accordingly
3319 if($sqlstr =~ /^\s*declare|begin/i) {
3320 print STDERR "\nPL/SQL procedure successfully completed.\n\n";
3321 } else {
3322 push(@formatbench, get_bench()) if $conf{extended_benchmarks};
3323 ($rows_affected, $success_code) = format_output($sth, $format, $num_rows,
3324 $sqlstr, $op, $op_text)
3325 or finish_query($sth), return();
3326 push(@formatbench, get_bench()) if $conf{extended_benchmarks};
3327 push(@totalbench, get_bench()) if !$conf{extended_benchmarks};
3329 finish_query($sth), return() if $sigintcaught; #pseudo sig handle
3331 # output format_affected
3332 if($result_output) {
3333 if(!$opt_batch) {
3334 print STDERR "\n" . format_affected($rows_affected, $success_code);
3337 if(!$opt_batch) {
3338 if($opt_bench || $conf{extended_benchmarks}) {
3339 print STDERR "\n\n";
3340 print STDERR ('-' x 80);
3341 print STDERR "\n";
3342 output_benchmark("Query: ", @querybench, "\n");
3343 output_benchmark("Format:", @formatbench, "\n");
3344 } else {
3345 output_benchmark(" (", @totalbench, ")");
3346 print STDERR "\n";
3348 print STDERR "\n";
3353 close(FOUT);
3355 finish_query($sth);
3357 undef($sth);
3358 undef($cursth);
3361 return($rows_affected, $success_code);
3364 sub wildcard_expand {
3365 my($sql) = @_;
3366 debugmsg(3, "wildcard_expand called", @_);
3368 my $newsql = $sql;
3369 my $fromstuff;
3370 my $wheregrouporder = $sql;
3371 $wheregrouporder =~ s/.*(where|order|group).*/\1/;
3372 if ($wheregrouporder eq $sql) {
3373 $wheregrouporder = "";
3375 ($sql,$fromstuff) = split(/order|group|where/i,$sql,2);
3376 if ($sql =~ /^select\s+(.+?)\*\s+from\s+(.+)/i) {
3377 debugmsg(1, "Match made: ($1) ($2)");
3378 my $wildcardstring = uc($1);
3379 my $tablename = uc($2);
3380 my @tlist = split(/,/,$tablename);
3381 my $tablelist = "";
3382 my %column_prefix;
3383 foreach my $table (@tlist) {
3384 $table =~ s/^ *//;
3385 $table =~ s/([^ ]+)\s+(.*)/\1/;
3386 $column_prefix{$table} = $2 ? $2 : $table;
3387 $tablelist .= ($tablelist ? "," : "") . $table;
3389 $tablelist =~ s/,/' or table_name='/g;
3390 my $qstr = "select table_name||'.'||column_name from all_tab_columns where (table_name='$tablelist') and column_name like '$wildcardstring%' escape '\\'";
3391 debugmsg(1, "qstr: [$qstr]");
3392 my $sth = $dbh->prepare($qstr);
3393 $sth->execute();
3394 setup_sigs();
3395 my $colname;
3396 my $collist;
3397 while ( ($colname) = $sth->fetchrow_array() ) {
3398 foreach my $table (keys %column_prefix) {
3399 $colname =~ s/$table\./$column_prefix{$table}\./;
3400 $colname =~ s/ //g;
3402 $collist .= ($collist ? "," : "") . $colname;
3404 $collist = $collist ? $collist : "*";
3405 $newsql = "select " . $collist . " from " . $tablename . " "
3406 . $wheregrouporder . " " . $fromstuff;
3407 debugmsg(1, "newsql: [$newsql]");
3409 $newsql;
3412 sub finish_query {
3413 my($sth) = @_;
3414 # This just finishes the query and cleans up the state info
3416 $sth->finish;
3417 undef($cursth);
3418 $running_query = 0;
3419 setup_sigs();
3422 sub get_bench {
3423 debugmsg(3, "get_bench called", @_);
3424 # returns benchmark info
3426 my($benchmark, $hires);
3427 $benchmark = new Benchmark;
3429 if($nohires) {
3430 $hires = time;
3431 } else {
3432 # use an eval to keep perl from syntax checking it unless we have the
3433 # Time::HiRes module loaded
3434 eval q{
3435 $hires = [gettimeofday]
3439 return($benchmark, $hires);
3442 sub output_benchmark {
3443 my($string, $bstart, $hrstart, $bend, $hrend, $string2) = @_;
3444 debugmsg(3, "output_benchmark called", @_);
3445 # This just outputs the benchmark info
3447 my $bench = timediff($bend, $bstart);
3449 my $time;
3450 if($nohires) {
3451 # the times will be seconds
3452 $time = $hrend - $hrstart;
3453 } else {
3454 eval q{$time = tv_interval($hrstart, $hrend)};
3455 $time = sprintf("%.2f", $time);
3458 unless($opt_bench || $conf{extended_benchmarks}) {
3459 # convert $time to something more readable
3460 $time =~ s/\.(\d+)$//;
3461 my $decimal = $1;
3462 my @tparts;
3463 my $tmp;
3464 if(($tmp = int($time / 604800)) >= 1) {
3465 push(@tparts, "$tmp week" . ($tmp != 1 && 's'));
3466 $time %= 604800;
3468 if(($tmp = int($time / 86400)) >= 1) {
3469 push(@tparts, "$tmp day" . ($tmp != 1 && 's'));
3470 $time %= 86400;
3472 if(($tmp = int($time / 3600)) >= 1) {
3473 push(@tparts, "$tmp hour" . ($tmp != 1 && 's'));
3474 $time %= 3600;
3476 if(($tmp = int($time / 60)) >= 1) {
3477 push(@tparts, "$tmp minute" . ($tmp != 1 && 's'));
3478 $time %= 60;
3480 $time ||= '0';
3481 $decimal ||= '00';
3482 $time .= ".$decimal";
3483 push(@tparts, "$time second" . ($time != 1 && 's'));
3484 $time = join(", ", @tparts);
3487 if($opt_bench || $conf{extended_benchmarks}) {
3488 print STDERR "$string\[ $time second" . ($time != 1 && 's')
3489 . " ] [" . timestr($bench) . " ]$string2";
3490 } else {
3491 print STDERR "$string$time$string2";
3495 sub format_output {
3496 my($sth, $format, $num_rows, $sqlstr, $op, $op_text) = @_;
3497 debugmsg(3, "format_output called", @_);
3498 # Formats the output according to the query terminator. If it was a ';' or
3499 # a '/' then a normal table is output. If it was a '\g' then all the columns # and rows are output put line by line.
3500 # input: $sth $format
3501 # sth is the statement handler
3502 # format can be either 'table', 'list', or 'list_aligned'
3503 # output: returns 0 on error, ($success_code, $rows_affected) on success
3504 # $success_code = ('select', 'affected');
3506 debugmsg(3,"type: [" . Dumper($sth->{TYPE}) . "]");
3508 # Is this query a select?
3509 my $isselect = 1 if $sqlstr =~ /^\s*select/i;
3511 if($format eq 'table') {
3512 my $count = 0;
3513 my $res = [];
3514 my $overflow = 0;
3515 while(my @res = $sth->fetchrow_array()) {
3516 push(@$res, \@res);
3517 $count++;
3518 if($count > 1000) {
3519 debugmsg(1,"overflow in table output, switching to serial mode");
3520 $overflow = 1;
3521 last;
3523 debugmsg(1,"num_rows hit on fetch") if $num_rows && $count >= $num_rows;
3524 last if $num_rows && $count >= $num_rows;
3525 return(0) if $sigintcaught; #pseudo sig handle
3528 # If we didn't get any rows back, then the query was probably an insert or
3529 # update, so we call format_affected
3530 if(@$res <= 0 && !$isselect) {
3531 return($sth->rows(), 'affected');
3534 return(0) if $sigintcaught; #pseudo sig handle
3536 # First go through all the return data to determine column widths
3537 my @widths;
3538 for( my $i = 0; $i < @{$res}; $i++ ) {
3539 for( my $j = 0; $j < @{$res->[$i]}; $j++ ) {
3540 if(length($res->[$i]->[$j]) > $widths[$j]) {
3541 $widths[$j] = length($res->[$i]->[$j]);
3544 return(0) if $sigintcaught; #pseudo sig handle
3545 debugmsg(1,"num_rows hit on calc") if $num_rows && $i >= $num_rows-1;
3546 last if $num_rows && $i >= $num_rows-1;
3549 return(0) if $sigintcaught; #pseudo sig handle
3551 my $fields = $sth->{NAME};
3552 my $types = $sth->{TYPE};
3553 my $nullable = $sth->{NULLABLE};
3555 debugmsg(4, "fields: [" . Dumper($fields) . "]");
3556 debugmsg(4, "types: [" . Dumper($types) . "]");
3557 debugmsg(4, "nullable: [" . Dumper($nullable) . "]");
3559 return(0) if $sigintcaught; #pseudo sig handle
3561 # Extend the column widths if the column name is longer than any of the
3562 # data, so that it doesn't truncate the column name
3563 for( my $i = 0; $i < @$fields; $i++ ) {
3564 if(length($fields->[$i]) > $widths[$i]) {
3565 debugmsg(3, "Extending $fields->[$i] for name width");
3566 $widths[$i] = length($fields->[$i]);
3568 return(0) if $sigintcaught; #pseudo sig handle
3571 return(0) if $sigintcaught; #pseudo sig handle
3573 # Extend the column widths if the column is NULLABLE so that we'll
3574 # have room for 'NULL'
3575 for( my $i = 0; $i < @$nullable; $i++ ) {
3576 if($nullable->[$i] && $widths[$i] < 4) {
3577 debugmsg(3, "Extending $fields->[$i] for null");
3578 $widths[$i] = 4;
3580 return(0) if $sigintcaught; #pseudo sig handle
3583 return(0) if $sigintcaught; #pseudo sig handle
3585 my $sumwidths;
3586 foreach(@widths) {
3587 $sumwidths += $_;
3590 return(0) if $sigintcaught; #pseudo sig handle
3592 debugmsg(2,"fields: [" . join("|", @$fields) . "] sumwidths: [$sumwidths] widths: [" . join("|", @widths) . "]\n");
3594 return(0) if $sigintcaught; #pseudo sig handle
3596 # now do the actual outputting, starting with the header
3597 my $rows_selected = 0;
3598 if(@$res) {
3599 if(!$opt_batch) {
3600 print FOUT "\r\e[K" if $op eq '<';
3601 print FOUT "\n";
3602 for( my $i = 0; $i < @$fields; $i++ ) {
3603 if($opt_batch) {
3604 print FOUT "\t" if $i > 0;
3605 print FOUT sprintf("%s", $fields->[$i]);
3607 else
3609 print FOUT " " if $i > 0;
3610 if($types->[$i] == 3 || $types->[$i] == 8) {
3611 print FOUT sprintf("%$widths[$i]s", $fields->[$i]);
3612 } else {
3613 print FOUT sprintf("%-$widths[$i]s", $fields->[$i]);
3617 print FOUT "\n";
3619 for( my $i = 0; $i < @$fields; $i++ ) {
3620 print FOUT " " if $i > 0;
3621 print FOUT '-' x $widths[$i];
3623 print FOUT "\n";
3626 return(0) if $sigintcaught; #pseudo sig handle
3628 # now print the actual data rows
3629 my $count = 0;
3630 for( my $j = 0; $j < @$res; $j++ ) {
3631 $count = $j;
3632 for( my $i = 0; $i < @$fields; $i++ ) {
3633 print FOUT " " if $i > 0;
3634 my $data = $res->[$j]->[$i];
3635 # Strip out plain ole \r's since SQL*Plus seems to...
3636 $data =~ s/\r//g;
3637 $data = 'NULL' unless defined $data;
3638 if($types->[$i] == 3 || $types->[$i] == 8) {
3639 print FOUT sprintf("%$widths[$i]s", $data);
3640 } else {
3641 print FOUT sprintf("%-$widths[$i]s", $data);
3644 print FOUT "\n";
3646 $rows_selected++;
3647 debugmsg(2,"num_rows hit on output") if $num_rows && $j >= $num_rows-1;
3648 last if $num_rows && $j >= $num_rows-1;
3649 return(0) if $sigintcaught; #pseudo sig handle
3652 if($overflow) {
3653 # output the rest of the data from the statement handler
3654 while(my $res = $sth->fetch()) {
3655 $count++;
3656 for( my $i = 0; $i < @$fields; $i++ ) {
3657 print FOUT " " if $i > 0;
3658 my $data = substr($res->[$i],0,$widths[$i]);
3659 # Strip out plain ole \r's since SQL*Plus seems to...
3660 $data =~ s/\r//g;
3661 $data = 'NULL' unless defined $data;
3662 if($types->[$i] == 3 || $types->[$i] == 8) {
3663 print FOUT sprintf("%$widths[$i]s", $data);
3664 } else {
3665 print FOUT sprintf("%-$widths[$i]s", $data);
3668 print FOUT "\n";
3670 $rows_selected++;
3671 debugmsg(2,"num_rows hit on output")
3672 if $num_rows && $count >= $num_rows-1;
3673 last if $num_rows && $count >= $num_rows-1;
3674 return(0) if $sigintcaught; #pseudo sig handle
3679 return($rows_selected, 'selected');
3681 } elsif($format eq 'list' || $format eq 'quiet-list' ) {
3682 # output in a nice list format, which is where we print each row in turn,
3683 # with each column on it's own line
3684 # quiet-list doesn't display *** Row...
3685 my $quiet = ($format eq 'quiet-list') ? 1 : 0;
3686 my $fields = $sth->{NAME};
3688 print "\r\e[K" if $op eq '<';
3689 print FOUT "\n";
3691 my $count = 0;
3692 while(my $res = $sth->fetch()) {
3693 print FOUT "\n**** Row: " . ($count+1) . "\n" unless ($quiet);
3694 for( my $i = 0; $i < @$fields; $i++ ) {
3695 my $data = $res->[$i];
3696 $data = 'NULL' unless defined $data;
3697 if ($quiet) {
3698 print FOUT $data . "\n";
3699 }else{
3700 print FOUT $fields->[$i] . ": " . $data . "\n";
3703 $count++;
3704 last if $num_rows && $count >= $num_rows;
3705 return(0) if $sigintcaught; #pseudo sig handle
3708 return(0) if $sigintcaught; #pseudo sig handle
3710 # If we didn't get any rows back, then the query was probably an insert or
3711 # update, so we call format_affected
3712 if($count <= 0 && !$isselect) {
3713 return($sth->rows(), 'affected');
3716 return($count, 'selected');
3718 } elsif($format eq 'list_aligned') {
3719 # output in a nice list format, which is where we print each row in turn,
3720 # with each column on it's own line. The column names are aligned in this
3721 # one (so that the data all starts on the same column)
3723 my $fields = $sth->{NAME};
3725 print "\r\e[K" if $op eq '<';
3726 print FOUT "\n";
3728 my $maxwidth = 0;
3729 for( my $i = 0; $i < @$fields; $i++ ) {
3730 my $len = length($fields->[$i]) + 1; # +1 for the colon
3731 $maxwidth = $len if $len >= $maxwidth;
3734 return(0) if $sigintcaught; #pseudo sig handle
3736 my $count = 0;
3737 while(my $res = $sth->fetch()) {
3738 print FOUT "\n**** Row: " . ($count+1) . "\n";
3739 for( my $i = 0; $i < @$fields; $i++ ) {
3740 my $data = $res->[$i];
3741 $data = 'NULL' unless defined $data;
3742 print FOUT sprintf("%-" . $maxwidth . "s", $fields->[$i] . ":");
3743 print FOUT " " . $data . "\n";
3745 $count++;
3746 last if $num_rows && $count >= $num_rows;
3747 return(0) if $sigintcaught; #pseudo sig handle
3750 return(0) if $sigintcaught; #pseudo sig handle
3752 # If we didn't get any rows back, then the query was probably an insert or
3753 # update, so we call format_affected
3754 if($count <= 0 && !$isselect) {
3755 return($sth->rows(), 'affected');
3758 return($count, 'selected');
3760 } elsif($format eq 'single_output') {
3761 # Outputs a single return column/row without any labeling
3763 print FOUT "\n";
3765 my $res = $sth->fetchrow_array();
3766 print FOUT "$res\n";
3768 my $count = ($res ? 1 : 0);
3770 return(0) if $sigintcaught; #pseudo sig handle
3772 return($count, 'selected');
3774 } elsif($format eq 'csv' || $format eq 'csv_no_header') {
3775 # output in a comma seperated values format. fields with a ',' are quoted
3776 # with '"' quotes, and rows are seperated by '\n' newlines
3778 print "\r\e[K" if $op eq '<';
3779 #print FOUT "\n";
3781 # check that Text::CSV_XS was included ok, if not output an error
3782 if($notextcsv) {
3783 soft_err("You must install Text::CSV_XS from CPAN to use this feature");
3784 return(0);
3785 } else {
3786 my $fields = $sth->{NAME};
3788 if($format eq 'csv') {
3789 # Print the column headers
3790 for(my $i = 0; $i < @$fields; $i++) {
3791 print FOUT "," if $i > 0;
3792 print FOUT $fields->[$i];
3794 print FOUT "\n";
3797 my $count = 0;
3798 while(my $res = $sth->fetch()) {
3799 $count++;
3801 $csv->combine(@$res);
3802 print FOUT $csv->string() . "\n";
3804 last if $num_rows && $count >= $num_rows;
3805 return(0) if $sigintcaught; #pseudo sig handle
3808 return(0) if $sigintcaught; #pseudo sig handle
3810 # If we didn't get any rows back, then the query was probably an insert or
3811 # update, so we call format_affected
3812 if($count <= 0 && !$isselect) {
3813 return($sth->rows(), 'affected');
3816 return($count, 'selected');
3818 } elsif($format eq 'sql') {
3819 # Produce SQL insert statements.
3820 print "\r" if $op eq '<';
3821 print FOUT "\n";
3823 my $cols = lc join(', ', @{$sth->{NAME}});
3824 my @types = map { scalar $dbh->type_info($_)->{TYPE_NAME} } @{ $sth->{TYPE} };
3825 my %warned_unknown_type;
3827 my $count = 0;
3828 while(my $res = $sth->fetch()) {
3829 $count++;
3830 die if @$res != @types;
3831 print FOUT "insert into TABLE ($cols) values (";
3832 foreach (0 .. $#$res) {
3833 my $t = $types[$_];
3834 my $v = $res->[$_];
3835 if (not defined $v) {
3836 print FOUT 'null';
3837 } else {
3838 if ($t eq 'DOUBLE' or $t eq 'DOUBLE PRECISION' or
3839 $t eq 'NUMBER' or $t eq 'DECIMAL') {
3840 die "bad number: $v" if $v !~ /\d/;
3841 print FOUT $v;
3842 } elsif ($t eq 'VARCHAR2' or $t eq 'CHAR' or $t eq 'CLOB') {
3843 $v =~ s/['']/''/g;
3844 print FOUT "'$v'";
3845 } elsif ($t eq 'DATE') {
3846 print FOUT "'$v'";
3847 } else {
3848 warn "don't know how to handle SQL type $t"
3849 unless $warned_unknown_type{$t}++;
3850 print FOUT "(unknown type $t: $v)";
3853 print FOUT ', ' unless $_ eq $#$res;
3855 print FOUT ");\n";
3856 last if $num_rows && $count >= $num_rows;
3857 return(0) if $sigintcaught; #pseudo sig handle
3859 return(0) if $sigintcaught; #pseudo sig handle
3861 # If we didn't get any rows back, then the query was probably an insert or
3862 # update, so we call format_affected
3863 if($count <= 0 && !$isselect) {
3864 return($sth->rows(), 'affected');
3866 return($count, 'selected');
3867 } else {
3868 die("Invalid format: $format");
3872 sub format_affected {
3873 my($rows_affected, $success_code) = @_;
3874 debugmsg(3, "format_affected called", @_);
3875 # This just outputs the given number
3877 return("$rows_affected row" . ($rows_affected == 1 ? '' : 's')
3878 ." $success_code");
3881 sub statusline {
3882 my($num, $max) = @_;
3883 debugmsg(3, "statusline called", @_);
3884 my $linewidth;
3885 eval q{
3886 use Term::ReadKey;
3887 (\$linewidth) = GetTerminalSize();
3889 if($@) {
3890 $linewidth = 80;
3892 my $numwidth = length($num);
3893 my $maxwidth = length($max);
3894 my $width = $linewidth - $numwidth - $maxwidth - 9;
3896 my $fillnum = (($num / $max) * $width);
3897 my $spacenum = ((($max - $num) / $max) * $width);
3899 if($fillnum =~ /\./) {
3900 $fillnum = int($fillnum) + 1;
3903 if($spacenum =~ /\./) {
3904 $spacenum = int($spacenum);
3907 my $fill = ('*' x $fillnum);
3908 my $space = ('-' x $spacenum);
3909 my $pcnt = sprintf("%.0d", ($num / $max * 100));
3911 return(sprintf("%-" . $linewidth . "s", "$num/$max [" . $fill . $space . "] $pcnt\%") . "\r");
3914 sub statusprint {
3915 my($string) = @_;
3917 return("\r\e[K$string\n");
3920 sub ping {
3921 debugmsg(3, "ping called", @_);
3922 if(!$dbh) {
3923 return(0);
3924 } else {
3925 # install alarm signal handle
3926 $SIG{ALRM} = \&sighandle;
3927 debugmsg(2, "Setting alarm for ping ($conf{connection_timeout} seconds)");
3928 alarm($conf{connection_timeout});
3930 debugmsg(2, "Pinging...");
3931 if($dbh->ping()) {
3932 debugmsg(2, "Ping successfull");
3933 alarm(0); # cancel alarm
3934 return(1);
3935 } else {
3936 debugmsg(2, "Ping failed");
3937 alarm(0); # cancel alarm
3938 db_reconnect();
3939 return(0);
3942 alarm(0); # cancel alarm
3945 sub query_err {
3946 my($query_type, $msg, $query) = @_;
3947 debugmsg(3, "query_err called", @_);
3948 # outputs a standard query error. does not exit
3949 # input: $query_type, $msg, $query
3951 chomp($query_type);
3952 chomp($msg);
3953 chomp($query);
3955 print STDERR "\n";
3956 print STDERR "$msg\n";
3957 print STDERR "Query: $query\n" if $query && $conf{sql_query_in_error};
3958 print STDERR "\n";
3961 sub lerr {
3962 my($msg) = @_;
3963 debugmsg(3, "err called", @_);
3964 # outputs an error message and exits
3966 print "Error: $msg\n";
3967 quit(1);
3970 sub soft_err {
3971 my($msg) = @_;
3972 debugmsg(3, "soft_err called", @_);
3973 # outputs a error, but doesn't exit
3975 print "\nError: $msg\n\n";
3978 sub wrn {
3979 my($msg) = @_;
3980 debugmsg(3, "wrn called", @_);
3981 # outputs a warning
3983 print STDERR "Warning: $msg\n";
3986 sub quit {
3987 my($exitcode, $force_quit, $msg) = @_;
3988 debugmsg(3, "quit called", @_);
3989 # just quits
3990 $exitcode ||= 0;
3991 $force_quit ||= 0; # Set this to 1 to try a smoother force quit
3992 $msg ||= '';
3994 setup_sigs();
3996 print "$msg" if $msg && $msg != "";
3997 $quitting = 1;
3999 if($force_quit) {
4000 exit($exitcode);
4003 commit_on_exit();
4005 # disconnect the database
4006 debugmsg(1, "disconnecting from database");
4007 if (defined $dbh) {
4008 $dbh->disconnect()
4009 or warn "Disconnect failed: $DBI::errstr\n";
4012 debugmsg(1, "exiting with exitcode: [$exitcode]");
4013 exit($exitcode);
4016 sub commit_on_exit {
4017 debugmsg(3, "commit_on_exit called", @_);
4019 # Commit... or not
4020 if($conf{commit_on_exit} && defined $dbh && !$dbh->{AutoCommit}) {
4021 # do nothing, oracle commits on disconnect
4022 } elsif(defined $dbh && !$dbh->{AutoCommit}) {
4023 print "Rolling back any outstanding transaction...\n";
4024 $dbh->rollback()
4025 or warn "Rollback failed: $DBI::errstr\n";
4029 sub debugmsg {
4030 my($debuglevel, @msgs) = @_;
4031 if($opt_debug >= $debuglevel ) {
4032 my @time = localtime();
4033 my $time = sprintf("%.4i-%.2i-%.2i %.2i:%.2i:%.2i", $time[5] + 1900,
4034 $time[4] + 1, $time[3], $time[2], $time[1], $time[0]);
4035 print STDERR "$time $debuglevel [" . join("] [", @msgs) . "]\n";
4039 sub usage {
4040 my($exit) = @_;
4041 debugmsg(3, "usage called", @_);
4043 $exit ||= 0;
4045 print <<_EOM_;
4046 Usage: yasql [options] [logon] [AS {SYSDBA|SYSOPER}] [@<file>[.ext]
4047 [<param1> <param2> ...]]
4048 Logon: <username>[/<password>][@<connect_string>] | /
4049 Options:
4050 -d, --debug=LEVEL Turn debugging on to LEVEL
4051 -H, --host=HOST Host to connect to
4052 -p, --port=PORT Host port to connect to
4053 -s, --sid=SID Oracle SID to connect to
4054 -h, -?, --help This help information
4055 -A, --nocomp Turn off building the auto-completion list
4056 -b, --bench, --benchmark Display extra benchmarking info
4057 -v, --version Print version and exit
4058 -B, --batch Batch mode (no headers, etc.)
4060 See the man pages for more help.
4061 _EOM_
4063 exit($exit);
4066 sub help {
4067 debugmsg(3, "help called", @_);
4068 # This just outputs online help
4070 my $help = <<_EOM_;
4072 Commands:
4073 help This screen
4074 quit, exit, \\q Exit the program.
4075 !<cmd>, host <cmd> Sends the command directly to a shell.
4076 \\A Regenerate the auto-completion list.
4077 connect [logon] [AS {SYSDBA|SYSOPER}]
4078 Open new connection.
4079 login = <username>[/<password>][@<connect_string>] | /
4080 reconnect, \\r Reconnect to the database
4081 desc[ribe] <object> Describe table, view, index, sequence, primary key,
4082 foreign key, constraint or trigger
4083 object = [<schema>.]<object>[\@dblink]
4084 show [all] <string> { like <name> }
4085 Shows [all] objects of a certain type
4086 string = tables, views, objects, sequences, clusters,
4087 dimensions, functions, procedures, packages,
4088 indexes, indextypes, libraries, snapshots,
4089 materialized views, synonyms, triggers,
4090 constraints
4091 name : use % for wildcard
4092 show <string> on|for <object>
4093 Shows properties for a particular object
4094 string = indexes, constraints, keys, checks, triggers,
4095 query, deps, ddl
4096 show invalid objects
4097 Shows all objects with status 'invalid'
4098 show processes Shows logged in users
4099 show locks Shows locks
4100 show [all] waits Shows [all] waits
4101 show plan Shows the last EXPLAIN PLAN ran
4102 show errors Shows errors from PL/SQL object creation
4103 l[ist], \\l, \\p List the contents of the current buffer
4104 cl[ear] [buffer], \\c
4105 Clear the current buffer
4106 ed[it] [filename], \\e [filename]
4107 Will open a text editor as defined by the EDITOR
4108 environment variable. If a file is given as the
4109 argument, then the editor will be opened with that
4110 file. If the given file does not exist then it will be
4111 created. In both cases the file will not be deleted,
4112 and the current buffer will be overwritten by the
4113 contents of the file. If no file is given, then the
4114 editor will be opened with a temporary file, which will
4115 contain the current contents of the buffer, or the last
4116 execute query if the buffer is empty. After the editor
4117 quits, the file will be read into the buffer. The
4118 contents will be parsed and executed just as if you had
4119 typed them all in by hand. You can have multiple
4120 commands and/or queries. If the last command is not
4121 terminated them you will be able to add furthur lines
4122 or input a terminator to execute the query.
4123 \@scriptname Execute all the commands in <filename> as if they were
4124 typed in directly. All CLI commands and queries are
4125 supported. yasql will quit after running all
4126 commands in the script.
4127 debug [num] Toggle debuggin on/off or if <num> is specified, then
4128 set debugging to that level
4129 autocommit Toggle AutoCommit on/off
4130 set <string> Set options
4131 string = [
4132 [long_read_len <size>]
4133 || [ fast_describe [on|off]]
4134 || [ serverout{put} [on|off] {size <size>} ]
4136 let <search string> Display all configurations
4138 Queries:
4139 All other input is treated as a query, and is sent straight to the database.
4141 All queries must be terminated by one of the following characters:
4142 ; - Returns data in table form
4143 / - Returns data in table form
4144 \\g - Returns data in non-aligned list form
4145 \\G - Returns data in aligned list form
4146 \\s - Returns data in CSV form. The first line is the column names
4147 \\S - Returns data in CSV form, but no column names
4148 \\i - Returns data in sql select commands form
4150 You may re-run the last query by typing the terminator by itself.
4152 Example:
4153 user\@ORCL> select * from table;
4154 user\@ORCL> \\g
4156 Return limit:
4157 You may add a number after the terminator, which will cause only the
4158 first <num> rows to be returned. e.g. 'select * from table;10' will run
4159 the query and return the first 10 rows in table format. This will also work
4160 if you just type the terminator to rerun the last query.
4162 Examples:
4163 The following will run the query, then run it again with different settings:
4164 user\@ORCL> select * from table;10
4165 user\@ORCL> \G50
4167 Redirection:
4168 You can add a shell like redirection operator after a query to pipe the output
4169 to or from a file.
4171 Output:
4172 You can use either '>' or '>>' to output to a file. '>' will overwrite the
4173 file and '>>' will append to the end of the file. The file will be created
4174 if it does not exist.
4176 Examples:
4177 user\@ORCL> select * from table; > table.dump
4178 user\@ORCL> select * from table\S > table.csv
4180 Input:
4181 You can use '<' to grab data from a CSV file. The file must be formatted
4182 with comma delimiters, quoted special fields, and rows seperated by
4183 newlines. When you use this operator with a query, the query will be ran
4184 for every line in the file. Put either '?' or ':n' (n being a number)
4185 placeholders where you want the data from the CSV file to be interpolated.
4186 The number of placeholders must match the number of columns in the CSV file.
4187 Each query is run as if you had typed it in, so the AutoCommit setting
4188 applies the same. If there is an error then the process will stop, but no
4189 rollback or anything will be done.
4191 Examples:
4192 user\@ORCL> insert into table1 values (?,?,?); < table1.csv
4193 user\@ORCL> update table2 set col1 = :1, col3 = :3, col2 = :2; < table2.csv
4195 Piping
4196 You can pipe the output from a query to the STDIN of any program you wish.
4198 Examples:
4199 user\@ORCL> select * from table; | less
4200 user\@ORCL> select * from table; | sort -n
4202 Please see 'man yasql' or 'perldoc yasql' for more help
4203 _EOM_
4205 my $ret = open(PAGER, "|$conf{pager}");
4206 if($ret) {
4207 print PAGER $help;
4208 close(PAGER);
4209 } else {
4210 print $help;
4214 sub get_object_type {
4215 debugmsg(3, "get_object_type", @_);
4216 my $object_name = shift;
4217 my $source = shift || 'ALL_OBJECTS';
4219 my $sqlstr = q{};
4220 my @data ;
4221 if (uc($source) eq 'ALL_OBJECTS'){
4222 $sqlstr = q{SELECT OBJECT_TYPE FROM all_objects WHERE OBJECT_NAME = ? AND OWNER= ? };
4223 @data = $dbh->selectrow_array($sqlstr, undef, uc($object_name), uc($dbuser));
4224 }elsif (uc($source) eq 'USER_OBJECTS' ) {
4225 $sqlstr = q{SELECT OBJECT_TYPE FROM USER_OBJECTS WHERE OBJECT_NAME = ? };
4226 @data = $dbh->selectrow_array($sqlstr, undef, uc($object_name));
4227 }elsif (uc($source) eq 'DBA_OBJECTS' ){
4228 query_err("internal", "get_object_type function doesn't support DBA_OBJECTS as source table!", "SELECT OBJECT_TYPE FROM DBA_OBJECTS.." );
4231 return shift @data
4234 __END__
4236 =head1 NAME
4238 yasql - Yet Another SQL*Plus replacement
4240 =head1 SYNOPSIS
4242 B<yasql> [options] [logon] [@<file>[.ext] [<param1> <param2>]
4244 =over 4
4246 =item logon
4248 <I<username>>[/<I<password>>][@<I<connect_string>>] | /
4250 =item options
4252 =over 4
4254 =item -d I<debuglevel>, --debug=I<debuglevel>
4256 Turn debuggin on to I<debuglevel> level. Valid levels: 1,2,3,4
4258 =item -H I<hostaddress>, --host=I<hostaddress>
4260 Host to connect to
4262 =item -p I<hostport>, --port=I<hostport>
4264 Host port to connect to
4266 =item -s I<SID>, --sid=I<SID>
4268 Oracle SID to connect to
4270 =item -h, -?, --help
4272 Output usage information and quit.
4274 =item -A, --nocomp
4276 Turn off the generation of the auto-completion list at startup. Use This if
4277 it takes too long to generate the list with a large database.
4279 =item -b, --bench, --benchmark
4281 Turn on extended benchmark info, which includes times and CPU usages for both
4282 queries and formatting.
4284 =item -v, --version
4286 Print version and exit
4288 =back
4290 =item Examples
4292 =over 4
4294 =item Connect to local database
4296 =over 4
4298 =item yasql
4300 =item yasql user
4302 =item yasql user/password
4304 =item yasql user@LOCAL
4306 =item yasql user/password@LOCAL
4308 =item yasql -h localhost
4310 =item yasql -h localhost -p 1521
4312 =item yasql -h localhost -p 1521 -s ORCL
4314 =back
4316 =item Connect to remote host
4318 =over 4
4320 =item yasql user@REMOTE
4322 =item yasql user/password@REMOTE
4324 =item yasql -h remote.domain.com
4326 =item yasql -h remote.domain.com -p 1512
4328 =item yasql -h remote.domain.com -p 1512 -s ORCL
4330 =back
4332 =back
4334 =back
4336 If no connect_string or a hostaddress is given, then will attempt to connect to
4337 the local default database.
4339 =head1 DESCRIPTION
4341 YASQL is an open source Oracle command line interface. YASQL features a much
4342 kinder alternative to SQL*Plus's user interface. This is meant to be a
4343 complete replacement for SQL*Plus when dealing with ad hoc queries and general
4344 database interfacing. It's main features are:
4346 =over 4
4348 =item Full ReadLine support
4350 Allows the same command line style editing as other ReadLine enabled programs
4351 such as BASH and the Perl Debugger. You can edit the command line as well as
4352 browse your command history. The command
4353 history is saved in your home directory in a file called .yasql_history. You
4354 can also use tab completion on all table and column names.
4356 =item Alternate output methods
4358 A different style of output suited to each type of need. There are currently
4359 table, list and CSV output styles. Table style outputs in the same manner as
4360 SQL*Plus, except the column widths are set based on the width of the data in
4361 the column, and not the column length defined in the table schema. List outputs
4362 each row on it's own line, column after column for easier viewing of wide return
4363 results. CSV outputs the data in Comma Seperated Values format, for easy
4364 import into many other database/spreadsheet programs.
4366 =item Output of query results
4368 You can easily redirect the output of any query to an external file
4370 =item Data Input and Binding
4372 YASQL allows you to bind data in an external CSV file to any query, using
4373 standard DBI placeholders. This is the ultimate flexibility when inserting or
4374 updating data in the database.
4376 =item Command pipes
4378 You can easily pipe the output of any query to an external program.
4380 =item Tab completion
4382 All tables, columns, and other misc objects can be completed using tab, much
4383 like you can with bash.
4385 =item Easy top rownum listings
4387 You can easily put a number after a terminator, which will only output those
4388 number of lines. No more typing "where rownum < 10" after every query. Now
4389 you can type 'select * from table;10' instead.
4391 =item Enhanced Data Dictionary commands
4393 Special commands like 'show tables', 'desc <table>', 'show indexes on <table>',
4394 'desc <sequence>', and many many more so that you can easily see your schema.
4396 =item Query editing
4398 You can open and edit queries in your favorite text editor.
4400 =item Query chaining
4402 You can put an abitrary number of queries on the same line, and each will be
4403 executed in turn.
4405 =item Basic scripting
4407 You can put basic SQL queries in a script and execute them from YASQL.
4409 =item Config file
4411 You can create a config file of options so that you don't have to set them
4412 everytime you run it.
4414 =item Future extensibility
4416 We, the community, can modify and add to this whatever we want, we can't do that
4417 with SQL*Plus.
4419 =back
4421 =head1 REQUIREMENTS
4423 =over 4
4425 =item Perl 5
4427 This was developed with Perl 5.6, but is known to work on 5.005_03 and above.
4428 Any earlier version of Perl 5 may or may not work. Perl 4 will definately not
4429 work.
4431 =item Unix environment
4433 YASQL was developed under GNU/Linux, and aimed at as many Unix installations as
4434 possible. Known to be compatible with GNU/Linux, AIX and Sun Solaris.
4435 Please send me an email (qzy@users.sourceforge.net) if it works for other platforms.
4436 I'd be especially interested if it worked on Win32.
4438 =item Oracle Server
4440 It has been tested and developed for Oracle8 and Oracle8i. There is atleast
4441 one issue with Oracle7 that I know of (see ISSUES below) and I have not tested
4442 it with Oracle9i yet.
4444 =item Oracle client libraries
4446 The Oracle client libraries must be installed for DBD::Oracle. Of course you
4447 can't install DBD::Oracle without them...
4449 =item DBD::Oracle
4451 DBD::Oracle must be installed since this uses DBI for database connections.
4453 =item ORACLE_HOME
4455 The ORACLE_HOME environment variable must be set if you use a connection
4456 descriptor to connect so that YASQL can translate the descriptor into
4457 usefull connection information to make the actual connection.
4459 =item ORACLE_SID
4461 The ORACLE_SID environment variable must be set unless you specify one with the
4462 -s option (see options above).
4464 =item Term::ReadLine
4466 Term::ReadLine must be installed (it is with most Perl installations), but more
4467 importantly, installing Term::ReadLine::Gnu from CPAN will greatly enhance the
4468 usability.
4470 =item Time::HiRes
4472 This is used for high resolution benchmarking. It is optional.
4474 =item Text::CSV_XS
4476 This perl module is required if you want to output CSV or input from CSV files.
4477 If you don't plan on using this features, then you don't need to install this
4478 module.
4480 =item Term::ReadKey
4482 This module is used for better input and output control. Right now it isn't
4483 required, but some parts of YASQL will look and function better with this
4484 installed.
4486 =back
4488 =head1 CONFIG
4490 YASQL will look for a config file first in ~/.yasqlrc then
4491 /etc/yasql.conf. The following options are available:
4493 =over 4
4495 =item connection_timeout = <seconds>
4497 Timeout for connection attempts
4499 Default: 20
4501 =item max_connection_attempts = <num>
4503 The amount of times to attempt the connection if the username/password are wrong
4505 Default: 3
4507 =item history_file = <file>
4509 Where to save the history file. Shell metachars will be globbed (expanded)
4511 Default: ~/.yasql_history
4513 =item pager = <file>
4515 Your favorite pager for extended output. (right now only the help command)
4517 Default: /bin/more
4519 =item auto_commit = [0/1]
4521 Autocommit any updates/inserts etc
4523 Default: 0
4525 =item commit_on_exit = [0/1]
4527 Commit any pending transactions on exit. Errors or crashes will still cause
4528 the current transaction to rollback. But with this on a commit will occur
4529 when you explicitly exit.
4531 Default: 0
4533 =item long_trunc_ok = [0/1]
4535 Long truncation OK. If set to 1 then when a row contains a field that is
4536 set to a LONG time, such as BLOB, CLOB, etc will be truncated to long_read_len
4537 length. If 0, then the row will be skipped and not outputted.
4539 Default: 1
4541 =item long_read_len = <num_chars>
4543 Long Read Length. This is the length of characters to truncate to if
4544 long_trunc_ok is on
4546 Default: 80
4548 =item edit_history = [0/1]
4550 Whether or not to put the query edited from the 'edit' command into the
4551 command history.
4553 Default: 1
4555 =item auto_complete = [0/1]
4557 Whether or not to generate the autocompletion list on connection. If connecting
4558 to a large database (in number of tables/columns sense), the generation process
4559 could take a bit. For most databases it shouldn't take long at all though.
4561 Default: 1
4563 =item extended_complete_list = [0/1]
4565 extended complete list will cause the possible matches list to be filled by
4566 basicly any and all objects. With it off the tab list will be restricted to
4567 only tables, columns, and objects owned by the current user.
4569 Default: 0
4571 =item complete_tables = [0/1]
4573 This controls whether or not to add tables to the completion list. This does
4574 nothing if auto_complete is set to 0.
4576 Default: 1
4578 =item complete_columns = [0/1]
4580 This controls whether or not to add columns to the completion list. This does
4581 nothing if auto_complete is set to 0.
4583 Default: 1
4585 =item complete_objects = [0/1]
4587 This controls whether or not to add all other objects to the completion list.
4588 This does nothing if auto_complete is set to 0. (Hint... depending on your
4589 schema this will include tables and columns also, so you could turn the other
4590 two off)
4592 Default: 1
4594 =item extended_benchmarks = [0/1]
4596 Whether or not to include extended benchmarking info after queries. Will
4597 include both execution times and CPU loads for both the query and formatting
4598 parts of the process.
4600 Default: 0
4602 =item prompt
4604 A string to include in the prompt. The prompt will always be suffixed by a
4605 '>' string. Interpolated variables:
4606 %H = connected host. will be prefixed with a '@'
4607 %U = current user
4609 Default: %U%H
4611 =item column_wildcards = [0/1]
4613 Column wildcards is an extremely experimental feature that is still being
4614 hashed out due to the complex nature of it. This should affect only select
4615 statements and expands any wildcards (*) in the column list. such as
4616 'select col* from table;'.
4618 Default: 0
4620 =item sql_query_in_error = [0/1]
4622 This this on to output the query in the error message.
4624 Default: 0
4626 =item nls_date_format = <string>
4628 Set the preferred NLS_DATE_FORMAT. This effects both date input and output
4629 formats. The default is ISO standard (YYYY-MM-DD HH24:MI:SS', not oracle
4630 default (YYYY-MM-DD).
4632 Default: YYYY-MM-DD HH24:MI:SS
4634 =item fast_describe
4636 Turn on fast describes. These are much faster than the old style of desc
4637 <table>, however non-built in datatypes may not be returned properly. i.e. a
4638 FLOAT will be returned as a NUMBER type. Internally FLOATs really are just
4639 NUMBERs, but this might present problems for you. If so, set this to 0
4641 Default: 1
4643 =back
4645 =head1 ISSUES
4647 =over 4
4649 =item Oracle7
4651 DBD::Oracle for Oracle8 may have issues connecting to an Oracle7 database. The
4652 one problem I have seen is that the use of placeholders in a query will cause
4653 oracle to issue an error "ORA-01008: not all variables bound". This will affect
4654 all of the hard-coded queries that I use such as the ones for the 'desc' and
4655 'show' commands. The queries that you type in on the command line may still
4656 work. The DBD::Oracle README mentions the use of the '-8' option to the
4657 'perl Makefile.PL' command to use the older Oracle7 OCI. This has not been
4658 tested.
4660 =back
4662 =head1 AUTHOR
4664 Originaly written by Nathan Shafer (B<nshafer@ephibian.com>) with support from
4665 Ephibian, Inc. http://www.ephibian.com
4666 Now it is mostly developed and maintained by Balint Kozman
4667 (B<qzy@users.sourceforge.net>). http://www.imind.hu
4669 =head1 THANKS
4671 Thanks to everyone at Ephibian that helped with testing, and a special thanks
4672 to Tom Renfro at Ephibian who did a lot of testing and found quite a few
4673 doozies.
4674 Also a lot of thanks goes to the mates at iMind.dev who keep suffering from
4675 testing new features on them.
4677 The following people have also contributed to help make YASQL what it is:
4678 Allan Peda, Lance Klein, Scott Kister, Mark Dalphin, Matthew Walsh
4680 And always a big thanks to all those who report bugs and problems, especially
4681 on other platforms.
4683 =head1 COPYRIGHT
4685 Copyright (C) 2000-2002 Ephibian, Inc., 2005 iMind.dev.
4688 =head1 LICENSE
4690 This program is free software; you can redistribute it and/or
4691 modify it under the terms of the GNU General Public License
4692 as published by the Free Software Foundation; either version 2
4693 of the License, or (at your option) any later version.
4695 This program is distributed in the hope that it will be useful,
4696 but WITHOUT ANY WARRANTY; without even the implied warranty of
4697 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4698 GNU General Public License for more details.
4700 You should have received a copy of the GNU General Public License
4701 along with this program; if not, write to the Free Software
4702 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
4704 =head1 TODO
4706 =over 4
4708 =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
4710 =item allow history to be saved based on host (as an option)
4712 =item make stifle_history a configurable option
4714 =item a row is printed after "Attempting to cancel query"
4716 =item reading from a script will not change prompt properly (for a script with no terminator)
4718 =item NULL stops printing after table goes into overflow or something
4720 =item extra space in \G... maybe others
4722 =item bug: tag completion doesn't work with caps anymore
4724 =item Add support for /NOLOG
4726 =item allow dblinks in show blah on blah commands
4728 =item show query doesn't work with schemas and db links
4730 =item add save and get buffer commands
4732 =item add R[UN] command (/ equivilent)
4734 =item add support for just 'connect' and prompt for username and password
4736 =item add PASSW[ORD] command for changing password
4738 =item add -s[ilent] command line to suppress all startup output and command prompts
4740 =item add 'start' command for scripting
4742 =item add 'run' synonum for '/'
4744 =item add 'show parameters <filter>' support
4746 =item fix segfaults when cancelling large outputs
4748 =item Add a 'SPOOL' command
4750 =item fix 'set...' commands
4752 =item Add variable bindings, prompting, control structures, etc.
4754 =item be able to describe any kind of object
4756 =item Add 'startup queries' in config file or support glogin.sql and login.sql
4758 =item fix case sensitive object names
4760 =item make win32 compliant
4762 =item add better error messages when the user can't access a data dictionary
4763 table
4765 =item add better error output, with line/col numbers and maybe a pointer.
4767 =item add chained ops, exactly like bash
4769 =item add plugins and hooks for all aspects.
4771 =item Add smarter tables and wrapping in columns. Also add configurable max
4772 column widths and max table width.
4774 =item Add a curses interface option for easy viewing and scrolling, etc. This
4775 will require some research to determine if it's even worth it.
4777 =item Add HTML output option
4779 =back
4781 =head1 CHANGELOG
4783 $Log: yasql,v $
4784 Revision 1.83 2005/05/09 16:57:13 qzy
4785 Fixed the 'DECIMAL' problem with describe command.
4786 Added sql mode with \i (patch by Ed Avis).
4787 Added redirectors (>, >>, |) to describe.
4788 Added 'show user' command.
4789 Added 'show uid' command.
4790 Added new makefile targets: clean, check. (patch by Ed Avis)
4791 Added "and owner = ?" to some show targets (patch by anonymous).
4792 Added command_complete_list feature and config option.
4793 Added disconnect command
4794 Added command completion: select, update, insert, delete, execute, etc.
4795 Added table.column name completion.
4796 Added feature to run tty-less (patch by Michael Kroell).
4797 Added a workaround for SunOS's alarm() bug (patch by Ed Avis).
4798 Fixed some minor issues in parser code.
4800 Revision 1.82 2005/02/18 16:57:13 qzy
4801 Added batch mode (ewl patch).
4802 Allow connections AS SYSDBA, AS SYSOPER and internal (sysdba patch by Derek Whayman).
4803 Added server_output to config options.
4804 Changed script execution to only add script lines to the query buffer (and not to history).
4806 Revision 1.81 2002/03/06 21:55:13 nshafer
4807 Fixed bug with password prompt.
4808 Added 'show plan' for outputting last explain plan results.
4809 Added 'show query' for viewing queries for views and materialized views.
4810 Optimized describes to be as fast as describes in SQL*Plus.
4811 Added new option 'fast_describe' on by default for new describe method.
4812 Added single_output as a formatting option for internal use.
4813 Fixed problem with password, quit, exit, \q getting added to the history list.
4814 Changed history to not add duplicate entries right next to each other.
4815 Added support for basic (non-returning) PL/SQL commands.
4816 Added support for create function, package, package body, prodedure, trigger.
4817 Added 'show errors' command
4818 Added 'conn' shortcut for 'connection'.
4819 Added 'exec[ute]' command.
4820 Added 'set serverout[put] on|off' command to mimic SQL*Plus's.
4821 Added alarms to pings in cases where DB connection is dropped and ping hangs.
4822 Cleaned up error messages.
4823 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.
4824 Changed quote escaping to be '' and "" instead of \' and \".
4825 Added full support for comments: rem[ark], --, and /* */.
4826 Right-justify works for the '8' datatype as well as '3' now.
4827 Re-worked debug output levels.
4828 Optimized query for completion lists a bit.
4829 Added completion-list limiting based on location in some DML statements (select, update, insert).
4830 Fixed up the display of '...' when generating tab completion list. Should work a lot better when hitting tab in the middle of the line.
4831 Added show views, objects, sequences, clusters, dimensions, functions, procedures, packages, indexes, indextypes, libraries, materialized views, snapshots, synonyms, triggers.
4832 Added show all <objects> command.
4833 Added type and owner columns to show commands.
4834 Fixed commit_on_exit logic.
4835 Added ability to use external authentication ('yasql /').
4836 The .sql extension for the scripting and editing commands are now optional.
4837 Fixed up editor execution to hopefully find the editor better.
4838 Added "Command" entry to "show processes".
4839 Added "show waits" and "show all waits" commands.
4840 Re-organized command line usage in anticipation for script parameters.
4841 Removed all uses of 'stty'.
4842 Added processing of STDIN, so redirects and pipes to YASQL work now.
4843 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
4844 Updated documentation.
4845 Fixed up alarm() calls.
4846 Fixed setting of NLS_DATE_FORMAT to apply on reconnects.
4847 Broke commands into 2 sets... ones that exectute any time, and ones that execute only when nothing is in the buffer
4848 Fixed printing of text read in from an edit command. It now echoes all of it.
4849 Now ignoring most SET commands so we don't tack them onto queries
4850 Fixed permissions in tarball
4852 Revision 1.80 2001/08/01 18:06:27 nshafer
4853 Fixed bug with delayed $term initialization\e\b
4855 Revision 1.79 2001/08/01 17:52:35 nshafer
4856 Fixed compatibility issues with the data dictionary in Oracle 7. Fixed ordering
4857 of indexes for compound indexes. Fixed display of objects from other schemas
4858 in some data dictionary commands such as 'show indexes on table'. (Thanks Nix)
4859 Fixed matching of declare and end in query string. Will not only match if on
4860 blank line. Fixed matching of '/' terminator in middle of queries. Will now
4861 only match if at end of line (Thanks Wesley Hertlein). Temp file for editing
4862 now appends '.sql' to end of temp file so that editors, like vim, automatically
4863 turn on syntax highlighting. Added searching of environment variable SQLPATH
4864 when looking for scripts. Terminal setup is now after script parsing, so that
4865 it will work when run under cron (Thanks David Zverina).
4867 Revision 1.78 2001/07/05 13:52:56 nshafer
4868 Fixed bug where parens were matching improperly.
4870 Revision 1.77 2001/07/04 02:57:08 nshafer
4871 Fixed bug where terminators wouldn't match if they were the next character
4872 after a quote character.
4874 Revision 1.76 2001/06/28 04:17:53 nshafer
4875 Term::ReadLine::Perl now supported, for what little functionality it does
4876 provide. Fixed segfault when hitting up when history is empty. Fixed bug
4877 when providing script names on command line (Thanks to Dave Zverina.)
4878 Rewrote the query parser to fix a bug, caused by the multiple-queries-on-one-
4879 line feature, that causes terminators, such as ';' and '/' to match when in
4880 quotes. When hitting tab on a line starting with a '@' for scripts, tab will
4881 now complete filenames and not database objects. Fixed DB timeout when
4882 prompting for username and password. Added support for 'DECLARE' keyword,
4883 however this does not mean that variable binding in PL/SQL blocks works yet.
4884 Sped up startup time a bit more (hopefully).
4886 Revision 1.75 2001/06/19 16:02:16 nshafer
4887 Fixed typo in error message for Term::ReadLine::Gnu
4888 Fixed crash when tab hit at username or password prompt
4889 Added -- as a comment type and fixed case where comment in quotes would
4890 match. (Mark Dalphin)
4891 Fixed 'desc' to also describe partitioned tables (Erik)
4893 Revision 1.74 2001/06/18 21:07:55 nshafer
4894 Fixed bug where / would not rerun last query (thanks Scott Kister)
4896 Revision 1.73 2001/05/23 18:35:17 nshafer
4897 Got rid of "Prototype mismatch" errors. Fixed typo in extended benchmarks
4899 Revision 1.72 2001/05/22 16:06:36 nshafer
4900 Fixed bug with error messages not displaying first time, and fixed bug with
4901 tab completion output
4903 Revision 1.71 2001/05/17 21:28:40 nshafer
4904 New CSV output format. Added CSV file input on any query. Added ability to
4905 pipe query results to any program. Added ability for multiple queries on one
4906 line. Changed tab completion generator to run first time you hit tab instead
4907 of on startup, which speeds up database connection. Now using SelfLoader to
4908 speed up loading and minimize memory use. Added a 'show plan for ____' command
4909 for easy display of explain plan output. Query times are now more readable
4910 and will split into weeks, days, hours, minutes, and seconds. Hopefully fixed
4911 some problems with stty and Solaris 2.4. Added support for 'rem' comments in
4912 scripts. Redirection output files are now shell expanded.
4914 Revision 1.70 2001/05/08 17:49:51 nshafer
4915 Fixed all places where a non-alphanumeric object name would break or not
4916 match.
4917 Added code for autoconf style installs.
4919 Revision 1.69 2001/05/07 23:47:47 nshafer
4920 fixed type
4922 Revision 1.68 2001/05/07 22:26:20 nshafer
4923 Fixed tab completion problems when completing objects with a $ in their name.
4924 Added config options complete_tables, complete_columns, and complete_objects,
4925 Added redirection of query output to file. Hopefully sped up exiting.
4926 Updated documentation.
4928 Revision 1.67 2001/05/04 17:35:04 nshafer
4929 YASQL will now suspend properly back to the shell when SIGTSTP is sent, as in
4930 when you hit ctrl-z on most systems. Added NLS_DATE_FORMAT setting in config
4931 file to support alter date views. Defaults to ISO standard. YASQL will now
4932 attempt to change it's process name, such as when viewed in ps or top. This
4933 will not work on all systems, nor is it a complete bullet proof way to hide
4934 your password if you provide it on the command line. But it helps to not
4935 make it so obvious to regular users. Scripts entered on the command line are
4936 now checked to be readable before attempting connection. A failed 'connect
4937 command will no long alter the prompt. Added \p option for printing the
4938 current buffer, ala psql. Large query results (over 1000 rows) are now
4939 handled MUCH better. YASQL will no longer try to hold more than 1000 rows in
4940 memory, which keeps it from sucking memory, and also improves the speed.
4941 When a query does return more than 1000 rows in table mode, those first 1000
4942 will determine the column widths, and all rows after that will get truncated.
4943 AIX has been reported to run YASQL perfectly.
4945 Revision 1.66 2001/03/13 21:34:58 nshafer
4946 There are no longer any references to termcap, so yasql should now work on
4947 termcap-less systems such as Debian Linux and AIX
4949 Revision 1.65 2001/03/12 17:44:31 nshafer
4950 Restoring the terminal is hopefully more robust and better now. YASQL now
4951 tries to use the 'stty' program to dump the settings of the terminal on
4952 startup so that it can restore it back to those settings. It requires that
4953 stty is installed in the path, but that should be the case with most systems.
4954 Also made the output of the query in the error message an option that is off
4955 by default. I had never meant to include that in the final release, but kept
4956 on forgetting to take it out.
4958 Revision 1.64 2001/03/06 16:00:33 nshafer
4959 Fixed bug where desc would match anytime, even in middle of query, which is
4960 bad.
4962 Revision 1.63 2001/03/01 17:30:26 nshafer
4963 Refined the ctrl-c process for not-so-linuxy OS's, namely solaris. Now
4964 stripping out Dos carriage returns since SQL*Plus seems to.
4966 Revision 1.62 2001/02/26 22:39:12 nshafer
4967 Fixed bug where prompt would reset itself when a blank line was entered.
4968 Added script argument on command line (Lance Klein)
4969 Added support for any command line commands in the script (Lance Klein)
4970 The 'desc' and 'show' commands no longer require a terminator (like ;) as long as the whole statement is on one line (Lance Klein)
4971 Added option 'extended_tab_list' for a much bigger, more complete tab listing (Lance Klein)
4972 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.
4973 cleaned up documentation a bit
4975 Revision 1.61 2001/01/31 19:56:22 nshafer
4976 changed CommitOnExit to be 1 by default, to emulate SQL*Plus behavior, and
4977 at popular request
4979 Revision 1.60 2001/01/29 16:38:17 nshafer
4980 got rid of (tm)
4982 Revision 1.59 2001/01/29 16:28:22 nshafer
4983 Modified docs a little with the new scope of open source now in the mix.
4985 Revision 1.58 2001/01/24 15:27:00 nshafer
4986 cleanup_after_signals is not in the Term::ReadLine::Stub, so it would
4987 output error messages on systems without Term::ReadLine::Gnu. Fixed
4989 Revision 1.57 2001/01/17 23:26:53 nshafer
4990 Added Tom Renfro's column_wildcard expansion code. New conf variable:
4991 column_wildcards. 0 by default until this code is expanded on a bit more.
4993 Revision 1.56 2001/01/17 23:00:25 nshafer
4994 Added CommitOnExit config, 0 by default. Added info output at startup and
4995 when a new connection is initiated about the state of AutoCommit and
4996 CommitOnExit. Also added statement about explicit rollback or commit when
4997 disconnecting. Added warning message to commit_cmd and rollback_cmd if
4998 AutoCommit is on. Now explicitly committing or rolling back on disconnect,
4999 it is no longer left up to the DBI's discretion... except in abnormal
5000 termination.
5002 Revision 1.55 2001/01/11 18:05:12 nshafer
5003 Added trap for regex errors in tab completion (like if you put 'blah[' then
5004 hit tab)
5006 Revision 1.54 2001/01/10 17:07:22 nshafer
5007 added output to those last 2 commands
5009 Revision 1.53 2001/01/10 17:03:58 nshafer
5010 added commit and rollback commands so that you don't have to send them to the
5011 backend
5013 Revision 1.52 2001/01/10 16:00:08 nshafer
5014 fixed bug with prompt where on each call get_prompt would add another '@'.
5015 Thanks Tom
5017 Revision 1.51 2001/01/09 21:16:12 nshafer
5018 dar... fixed another bug where the %H would stay if there was no prompt_host
5020 Revision 1.50 2001/01/09 21:12:13 nshafer
5021 fixed bug with that last update. Now it only interpolates the %H variable
5022 if there is something to interpolate it with
5024 Revision 1.49 2001/01/09 21:09:56 nshafer
5025 changed the %H variable to be prefixed with a @
5027 Revision 1.48 2001/01/09 21:04:36 nshafer
5028 changed 'default' to '' for the prompt's hostname when no connect_string is
5029 used
5031 Revision 1.47 2001/01/09 20:55:11 nshafer
5032 added configurable prompt and changed the default prompt
5034 Revision 1.46 2001/01/09 18:50:50 nshafer
5035 updated todo list
5037 Revision 1.45 2001/01/09 18:32:35 nshafer
5038 Added 'connect <connect_string>' command. I may add the ability to specify
5039 options like on the command line (like '-H blah.com')
5041 Revision 1.44 2001/01/08 22:08:49 nshafer
5042 more documentation changes
5044 Revision 1.43 2001/01/08 20:51:31 nshafer
5045 added some documentation
5047 Revision 1.42 2001/01/08 20:09:35 nshafer
5048 Added debug and autocommit commands
5050 Revision 1.41 2001/01/08 18:12:43 nshafer
5051 added END handler to hopefully clean up the terminal better
5053 Revision 1.40 2001/01/05 23:29:38 nshafer
5054 new name!
5056 Revision 1.39 2001/01/05 18:00:16 nshafer
5057 Added config file options for auto completion generation and extended
5058 benchmark info
5060 Revision 1.38 2001/01/05 16:39:47 nshafer
5061 Fixed error where calling edit a second time would not open the file properly
5062 because of the way glob() works.
5064 Revision 1.37 2001/01/04 23:52:30 nshafer
5065 changed the version string to parse it out of the revision string (duh...)
5066 moved the prompting of username and password so that the check for the
5067 oracle_home variable happens before. Before if you didn't have the environment
5068 variable set then it will prompt you for username and password, then die
5069 with the error, which is annoying
5070 fixed the quit calls so taht they properly erase the quit line from the
5071 history. I had broken this a long time ago when I added the exit status
5072 param to the quit function
5073 Outputting in full table format (';' terminator) with a num_rows number
5074 (like ';100') would still cause the entire result set to be pulled into
5075 memory, which was really slow and could take a lot of memory if the table
5076 was large. Fixed it so that it only pulls in num_rows number of rows when
5077 using the digit option
5079 Revision 1.36 2000/12/22 22:12:18 nshafer
5080 fixed a wrong-quote-type in the debug messages
5082 Revision 1.35 2000/12/22 22:07:06 nshafer
5083 forgot version... you know the drill...
5085 Revision 1.34 2000/12/22 21:57:01 nshafer
5086 Added config file support, queries from the 'edit' command are now entered
5087 into the command history (configurable), cleaned up the SIGINT actions quite
5088 a bit so they should work better now, added LongReadLen and LongTruncOk
5089 options so that LONG columns types won't mess up, added the number after terminator
5090 feature to limit how many rows are returned.
5092 Revision 1.33 2000/12/20 22:56:03 nshafer
5093 version number.... again.... sigh
5095 Revision 1.32 2000/12/20 22:55:32 nshafer
5096 added todo item, now in rpms
5098 Revision 1.31 2000/12/20 17:07:52 nshafer
5099 added the reprompt for username/password on error 1005 null password given
5101 Revision 1.30 2000/12/20 17:04:18 nshafer
5102 Refined the shadow_redisplay stuff. Now I will only use my builtin function
5103 if the terminal type is set to "xterm" because that terminal type has a
5104 broken termcap entry. Also set it to not echo when entering password if
5105 Term::ReadLine::Gnu is not installed
5107 Revision 1.29 2000/12/20 15:47:56 nshafer
5108 trying a new scheme for the shadow_redisplay. Clear to EOL wasn't working
5109 Also fixed a few problems in the documentation
5112 Revision 1.28 2000/12/19 23:55:03 nshafer
5113 I need to stop forgetting the revision number...
5115 Revision 1.27 2000/12/19 23:48:49 nshafer
5116 cleaned up debugging
5118 Revision 1.26 2000/12/19 23:10:18 nshafer
5119 Lotsa new stuff... tab completion of table, column, and object names,
5120 improved signal handling, the edit command now accepts a filename parameter,
5121 new command 'show processes' which shows you info on who's connected,
5122 improved benchmark info, and a lot of other cleanup/tweaks
5124 Revision 1.25 2000/12/13 16:58:26 nshafer
5125 oops forgot documentation again
5127 Revision 1.24 2000/12/13 16:54:42 nshafer
5128 added desc <trigger>
5130 Revision 1.23 2000/12/12 17:52:15 nshafer
5131 updated todo list (oops, forgot)
5133 Revision 1.22 2000/12/12 17:51:39 nshafer
5134 added desc <index>
5136 Revision 1.21 2000/12/12 17:15:28 nshafer
5137 fixed bug when connecting using a host string (-H option)
5138 added a few more types to the 'show' and 'desc' commands
5140 Revision 1.20 2000/12/08 22:13:43 nshafer
5141 many little fixes and tweaks here and there
5143 Revision 1.19 2000/12/06 20:50:03 nshafer
5144 added scripting ability with "@<filename>" command
5145 changed all tabs to spaces!
5147 Revision 1.18 2000/12/06 19:30:38 nshafer
5148 added clear command
5149 refined connection process. if invalid username/password entered then prompt again
5151 Revision 1.17 2000/12/05 22:20:58 nshafer
5152 Tightened up outputs. Doesn't show column names if no rows selected, if
5153 it's not a select, then show number of rows affected
5155 Revision 1.16 2000/12/04 18:04:53 nshafer
5156 *** empty log message ***
5158 Revision 1.15 2000/12/04 18:03:14 nshafer
5159 fixed bug where the -H option was interpreted as -h or help. All command
5160 line options are now case sensitive
5162 Revision 1.14 2000/12/04 17:54:38 nshafer
5163 Added list command (and \l and l)
5165 Revision 1.13 2000/12/04 17:34:18 nshafer
5166 fixed a formatting issue if Time::HiRes isn't installed
5168 Revision 1.12 2000/12/04 17:29:41 nshafer
5169 Added benchmark options to view the extended benchmark info. Now it displays
5170 just the time in a more friendly format. The old style is only active if the
5171 benchmark option is specified.
5172 Cleaned up some formatting issues
5173 Brought the usage and POD documentation up to date
5174 Added some items to the TODO
5176 Revision 1.11 2000/11/30 22:54:38 nshafer
5177 Fixed bug with the edit command where if you were 'inquotes' then you would
5178 stay in quotes even after editing the file
5180 Revision 1.10 2000/11/30 22:01:38 nshafer
5181 Fixed bug where username and password were added to the command history.
5182 Set it so that the quit commands are not added to the command history either.
5183 Added the 'edit' command and modified it's todo list item, as well as added
5184 it to the 'help' command
5186 Revision 1.9 2000/11/29 17:55:35 nshafer
5187 changed version from .21 to 1.0 beta 9. I'll follow the revision numbers now
5189 Revision 1.8 2000/11/29 17:46:31 nshafer
5190 added a few items to the todo list
5192 Revision 1.7 2000/11/29 15:50:56 nshafer
5193 got rid of SID output at startup
5195 Revision 1.6 2000/11/29 15:49:51 nshafer
5196 moved revision info to $revision and added Id output
5198 Revision 1.5 2000/11/29 15:46:41 nshafer
5199 fixed revision number
5201 Revision 1.4 2000/11/29 15:44:23 nshafer
5202 fixed issue where environment variable ORACLE_SID overwrote explicit set
5203 on the command line. now whatever you put on the command line will overwrite
5204 the environment variable
5206 =cut