Add default config handler in set function
[yasql.git] / yasql.in
blobc2d1f151402e691cb0408e00159e5638efe97ad5
1 #! /usr/bin/env perl
2 # vim: set tabstop=2 smartindent shiftwidth=2 expandtab :
4 # Name: yasql - Yet Another SQL*Plus replacement
6 # See POD documentation at end
8 # $Id: yasql,v 1.83 2005/05/09 16:57:13 qzy Exp qzy $
10 # Copyright (C) 2000 Ephibian, Inc.
11 # Copyright (C) 2005 iMind.dev, Inc.
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; either version 2
16 # of the License, or (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 # Yasql was originally developed by Nathan Shafer at Ephibian, Inc.
28 # Now it is mainly developed and maintained by Balint Kozman at iMind.dev, Inc.
30 # email: nshafer@ephibian.com
31 # email: qzy@users.sourceforge.net
32 # email: jpnangle@users.sourceforge.net
35 use strict;
37 use SelfLoader;
39 use DBI;
40 use Term::ReadLine;
41 use Data::Dumper;
42 use Benchmark;
43 use Getopt::Long;
45 # Load DBD::Oracle early to work around SunOS bug. See
46 # http://article.gmane.org/gmane.comp.lang.perl.modules.dbi.general/207
48 require DBD::Oracle;
50 #Globals
51 use vars qw(
52 $VERSION $Id $dbh $cursth @dbparams $dbuser $dbversion $term $term_type
53 $features $attribs $last_history $num_connects $connected $running_query
54 @completion_list @completion_possibles $completion_built $opt_host $opt_sid
55 $opt_port $opt_debug $opt_bench $opt_nocomp $opt_version $qbuffer
56 $last_qbuffer $fbuffer $last_fbuffer $quote $inquotes $inplsqlblock $increate
57 $incomment $csv_filehandle_open $csv_max_lines $nohires $notextcsv $csv
58 $sysconf $sysconfdir $quitting $sigintcaught %conf %prompt $prompt_length
59 @sqlpath %set $opt_batch $opt_notbatch $opt_headers
62 select((select(STDOUT), $| = 1)[0]); #unbuffer STDOUT
64 $sysconfdir = "/etc";
65 $sysconf = "$sysconfdir/yasql.conf";
67 # try to include Time::HiRes for fine grained benchmarking
68 eval q{
69 use Time::HiRes qw (gettimeofday tv_interval);
72 # try to include Text::CSV_XS for input and output of CSV data
73 eval q{
74 use Text::CSV_XS;
76 if($@) {
77 $notextcsv = 1;
80 # install signal handlers
81 sub setup_sigs {
82 $SIG{INT} = \&sighandle;
83 $SIG{TSTP} = 'DEFAULT';
84 $SIG{TERM} = \&sighandle;
86 setup_sigs();
88 # install a filter on the __WARN__ handler so that we can get rid of
89 # DBD::Oracle's stupid ORACLE_HOME warning. It would warn even if we don't
90 # connect using a TNS name, which doesn't require access to the ORACLE_HOME
91 $SIG{__WARN__} = sub{
92 warn(@_) unless $_[0] =~ /environment variable not set!/;
95 # initialize the whole thing
96 init();
98 if($@) {
99 if(!$opt_batch) {
100 wrn("Time::HiRes not installed. Please install if you want benchmark times "
101 ."to include milliseconds.");
103 $nohires = 1;
107 $connected = 1;
109 # start the interface
110 interface();
112 # end
114 ################################################################################
115 ########### non-self-loaded functions ########################################
117 sub BEGIN {
118 $Id = '$Id: yasql,v 1.83 2005/05/09 02:07:13 nshafer Exp nshafer $';
119 ($VERSION) = $Id =~ /Id: \S+ (\d+\.\d+)/;
122 sub argv_sort {
123 if($a =~ /^\@/ && $b !~ /^\@/) {
124 return 1;
125 } elsif($a !~ /^\@/ && $b =~ /^\@/) {
126 return -1;
127 } else {
128 return 0;
132 sub sighandle {
133 my($sig) = @_;
134 debugmsg(3, "sighandle called", @_);
136 $SIG{$sig} = \&sighandle;
138 if($sig =~ /INT|TERM|TSTP/) {
139 if($quitting) {
140 # then we've already started quitting and so we just try to force exit
141 # without the graceful quit
142 print STDERR "Attempting to force exit...\n";
143 exit();
146 if($sigintcaught) {
147 # the user has alrady hit INT and so we now force an exit
148 print STDERR "Caught another SIG$sig\n";
149 quit(undef, 1);
150 } else {
151 $sigintcaught = 1;
154 if($running_query) {
155 if(defined $cursth) {
156 print STDERR "Attempting to cancel query...\n";
157 debugmsg(1, "canceling statement handle");
158 my $ret = $cursth->cancel();
159 $cursth->finish;
161 } elsif(!$connected) {
162 quit();
164 if(defined $cursth) {
165 print STDERR "Attempting to cancel query...\n";
166 debugmsg(1, "canceling statement handle");
167 my $ret = $cursth->cancel();
168 $cursth->finish;
172 } elsif($sig eq 'ALRM') {
174 if(defined $dbh) {
175 wrn("Connection lost (timeout: $conf{connection_timeout})");
176 quit(1);
177 } else {
178 lerr("Could not connect to database, timed out. (timeout: "
179 ."$conf{connection_timeout})");
184 sub END {
185 debugmsg(3, "END called", @_);
187 # save the history buffer
188 if($term_type && $term_type eq 'gnu' && $term->history_total_bytes()) {
189 debugmsg(1, "Writing history");
190 unless($term->WriteHistory($conf{history_file})) {
191 wrn("Could not write history file to $conf{history_file}. "
192 ."History not saved");
197 ################################################################################
198 ########### self-loaded functions ##############################################
200 #__DATA__
202 sub init {
203 # call GetOptions to parse the command line
204 my $opt_help;
205 Getopt::Long::Configure( qw(permute) );
206 $Getopt::Long::ignorecase = 0;
207 usage(1) unless GetOptions(
208 "debug|d:i" => \$opt_debug,
209 "host|H=s" => \$opt_host,
210 "port|p=s" => \$opt_port,
211 "sid|s=s" => \$opt_sid,
212 "help|h|?" => \$opt_help,
213 "nocomp|A" => \$opt_nocomp,
214 "bench|benchmark|b" => \$opt_bench,
215 "version|V" => \$opt_version,
216 "batch|B" => \$opt_batch,
217 "interactive|I" => \$opt_notbatch,
220 # set opt_debug to 1 if it's defined, which means the user just put -d or
221 # --debug without an integer argument
222 $opt_debug = 1 if !$opt_debug && defined $opt_debug;
224 $opt_batch = 0 if $opt_notbatch;
226 $opt_batch = 1 unless defined $opt_batch || -t STDIN;
228 debugmsg(3, "init called", @_);
229 # This reads the command line then initializes the DBI and Term::ReadLine
230 # packages
232 $sigintcaught = 0;
233 $completion_built = 0;
235 usage(0) if $opt_help;
237 # Output startup string
238 if(!$opt_batch) {
239 print STDERR "\n";
240 print STDERR "YASQL version $VERSION Copyright (c) 2000-2001 Ephibian, Inc, 2005 iMind.dev.\n";
241 print STDERR '$Id: yasql,v 1.83 2005/05/09 02:07:13 qzy Exp qzy $' . "\n";
244 if($opt_version) {
245 print STDERR "\n";
246 exit(0);
249 if(!$opt_batch) {
250 print STDERR "Please type 'help' for usage instructions\n";
251 print STDERR "\n";
254 # parse the config files. We first look for ~/.yasqlrc, then
255 # /etc/yasql.conf
256 # first set up the defaults
257 %conf = (
258 connection_timeout => 20,
259 max_connection_attempts => 3,
260 history_file => '~/.yasql_history',
261 pager => '/bin/more',
262 auto_commit => 0,
263 commit_on_exit => 1,
264 long_trunc_ok => 1,
265 long_read_len => 80,
266 edit_history => 1,
267 auto_complete => 1,
268 extended_benchmarks => 0,
269 prompt => '%U%H',
270 column_wildcards => 0,
271 extended_complete_list => 0,
272 command_complete_list => 1,
273 sql_query_in_error => 0,
274 nls_date_format => 'YYYY-MM-DD HH24:MI:SS',
275 complete_tables => 1,
276 complete_columns => 1,
277 complete_objects => 1,
278 fast_describe => 1,
279 server_output => 2000,
282 my $config_file;
283 if( -e $ENV{YASQLCONF} ) {
284 $config_file = $ENV{YASQLCONF};
285 } elsif(-e "$ENV{HOME}/.yasqlrc") {
286 $config_file = "$ENV{HOME}/.yasqlrc";
287 } elsif(-e $sysconf) {
288 $config_file = $sysconf;
291 if($config_file) {
292 debugmsg(2, "Reading config: $config_file");
293 open(CONFIG, "$config_file");
294 while(<CONFIG>) {
295 chomp;
296 s/#.*//;
297 s/^\s+//;
298 s/\s+$//;
299 next unless length;
300 my($var, $value) = split(/\s*=\s*/, $_, 2);
301 $var = 'auto_commit' if $var eq 'AutoCommit';
302 $var = 'commit_on_exit' if $var eq 'CommitOnExit';
303 $var = 'long_trunc_ok' if $var eq 'LongTruncOk';
304 $var = 'long_read_len' if $var eq 'LongReadLen';
305 $conf{$var} = $value;
306 debugmsg(3, "Setting option [$var] to [$value]");
310 if (($conf{server_output} > 0) && ($conf{server_output} < 2000)) {
311 $conf{server_output} = 2000;
313 if ($conf{server_output} > 1000000) {
314 $conf{server_output} = 1000000;
317 ($conf{history_file}) = glob($conf{history_file});
319 debugmsg(3,"Conf: [" . Dumper(\%conf) . "]");
321 # Create a Text::CSV object
322 unless($notextcsv) {
323 $csv = new Text::CSV_XS( { binary => 1 } );
326 # Change the process name to just 'yasql' to somewhat help with security.
327 # This is not bullet proof, nor is it supported on all platforms. Those that
328 # don't support this will just fail silently.
329 debugmsg(2, "Process name: $0");
330 $0 = 'yasql';
332 # Parse the SQLPATH environment variable if it exists
333 if($ENV{SQLPATH}) {
334 @sqlpath = split(/;/, $ENV{SQLPATH});
337 # If the user set the SID on the command line, we'll overwrite the
338 # environment variable so that DBI sees it.
339 #print "Using SID $opt_sid\n" if $opt_sid;
340 $ENV{ORACLE_SID} = $opt_sid if $opt_sid;
342 # output info about the options given
343 print STDERR "Debugging is on\n" if $opt_debug;
344 DBI->trace(1) if $opt_debug > 3;
346 # Extending on from Oracle's conventions, try and obtain an early indication
347 # of ora_session_mode from AS SYSOPER, AS SYSDBA options. Be flexible :-)
348 my $ora_session_mode = 0;
349 my $osmp = '';
350 if (lc($ARGV[-2]) eq 'as') {
351 $ora_session_mode = 2 if lc($ARGV[-1]) eq 'sysdba';
352 $ora_session_mode = 4 if lc($ARGV[-1]) eq 'sysoper';
353 pop @ARGV;
354 pop @ARGV;
355 } elsif (lc($ARGV[1]) eq 'as') {
356 $ora_session_mode = 2 if lc($ARGV[2]) eq 'sysdba';
357 $ora_session_mode = 4 if lc($ARGV[2]) eq 'sysoper';
358 @ARGV = ($ARGV[0], @ARGV[3..$#ARGV]);
361 # set up DBI
362 if(@ARGV == 0) {
363 # nothing was provided
364 debugmsg(2, "No command line args were found");
365 $dbh = db_connect(1, $ora_session_mode);
366 } else {
367 debugmsg(2, "command line args found!");
368 debugmsg(2, @ARGV);
369 # an argument was given!
371 my $script = 0;
372 if(substr($ARGV[0], 0, 1) eq '@') {
373 # no logon string was given, must be a script
374 debugmsg(2, "Found: no logon, script name");
375 my($script_name, @script_params) = @ARGV;
376 $script = 1;
378 $dbh = db_connect(1, $ora_session_mode);
380 run_script($script_name);
381 } elsif(substr($ARGV[0], 0, 1) ne '@' && substr($ARGV[1], 0, 1) eq '@') {
382 # A logon string was given as well as a script file
383 debugmsg(2, "Found: login string, script name");
384 my($logon_string, $script_name, @script_params) = @ARGV;
385 $script = 1;
387 my($ora_session_mode2, $username, $password, $connect_string)
388 = parse_logon_string($logon_string);
389 $ora_session_mode = $ora_session_mode2 if $ora_session_mode2;
390 $dbh = db_connect(1, $ora_session_mode, $username, $password, $connect_string);
392 run_script($script_name);
393 } elsif(@ARGV == 1 && substr($ARGV[0], 0, 1) ne '@') {
394 # only a logon string was given
395 debugmsg(2, "Found: login string, no script name");
396 my($logon_string) = @ARGV;
398 my($ora_session_mode2, $username, $password, $connect_string)
399 = parse_logon_string($logon_string);
400 $ora_session_mode = $ora_session_mode2 if $ora_session_mode2;
401 $dbh = db_connect(1, $ora_session_mode, $username, $password, $connect_string);
402 } else {
403 usage(1);
406 if ($conf{server_output} > 0) {
407 $dbh->func( $conf{server_output}, 'dbms_output_enable' );
408 $set{serveroutput} = 1;
411 # Quit if one or more scripts were given on the command-line
412 quit(0) if $script;
415 if (!$opt_batch) {
416 setup_term() unless $term;
419 # set up the pager
420 $conf{pager} = $ENV{PAGER} if $ENV{PAGER};
423 sub setup_term {
424 # set up the Term::ReadLine
425 $term = new Term::ReadLine('YASQL');
426 $term->ornaments(0);
427 $term->MinLine(0);
429 debugmsg(1, "Using " . $term->ReadLine());
431 if($term->ReadLine eq 'Term::ReadLine::Gnu') {
432 # Term::ReadLine::Gnu specific setup
433 $term_type = 'gnu';
435 $attribs = $term->Attribs();
436 $features = $term->Features();
438 $term->stifle_history(500);
439 if($opt_debug >= 4) {
440 foreach(sort keys(%$attribs)) {
441 debugmsg(4,"[term-attrib] $_: $attribs->{$_}");
443 foreach(sort keys(%$features)) {
444 debugmsg(4,"[term-feature] $_: $features->{$_}");
448 # read in the ~/.yasql_history file
449 if(-e $conf{history_file}) {
450 unless($term->ReadHistory($conf{history_file})) {
451 wrn("Could not read $conf{history_file}. History not restored");
453 } else {
454 print STDERR "Creating $conf{history_file} to store your command line history\n";
455 open(HISTORY, ">$conf{history_file}")
456 or wrn("Could not create $conf{history_file}: $!");
457 close(HISTORY);
460 $last_history = $term->history_get($term->{history_length});
462 $attribs->{completion_entry_function} = \&complete_entry_function;
463 my $completer_word_break_characters
464 = $attribs->{completer_word_break_characters};
465 $completer_word_break_characters =~ s/[a-zA-Z0-9_\$\#]//g;
466 $attribs->{completer_word_break_characters}
467 = $completer_word_break_characters;
468 #$attribs->{catch_signals} = 0;
469 } elsif($term->ReadLine eq 'Term::ReadLine::Perl') {
470 # Term::ReadLine::Perl specific setup
471 $term_type = 'perl';
472 if($opt_debug >= 4) {
473 foreach(sort keys(%{$term->Features()})) {
474 debugmsg(4,"[term-feature] $_: $attribs->{$_}");
480 if ($term->ReadLine eq 'Term::ReadLine::Stub') {
481 wrn("Neither Term::ReadLine::Gnu or Term::ReadLine::Perl are installed.\n"
482 . "Please install from CPAN for advanced functionality. Until then "
483 . "YASQL will run\ncrippled. (like possibly not having command history "
484 . "or line editing...\n");
488 sub parse_logon_string {
489 debugmsg(3, "parse_logon_string called", @_);
491 my($arg) = @_;
492 my($ora_session_mode, $username, $password, $connect_string);
494 # strip off AS SYSDBA / AS SYSOPER first
495 if($arg =~ /^(.*)\s+as\s+sys(\w+)\s*$/i) {
496 $ora_session_mode = 2 if lc($2) eq 'dba';
497 $ora_session_mode = 4 if lc($2) eq 'oper';
498 $arg = $1 if $ora_session_mode;
499 $ora_session_mode = 0 unless $ora_session_mode;
501 if($arg =~ /^\/$/) {
502 $username = '';
503 $password = '';
504 $connect_string = 'external';
505 return($ora_session_mode, $username, $password, $connect_string);
506 } elsif($arg eq 'internal') {
507 $username = '';
508 $password = '';
509 $connect_string = 'external';
510 $ora_session_mode = 2;
511 return($ora_session_mode, $username, $password, $connect_string);
512 } elsif($arg =~ /^([^\/]+)\/([^\@]+)\@(.*)$/) {
513 #username/password@connect_string
514 $username = $1;
515 $password = $2;
516 $connect_string = $3;
517 return($ora_session_mode, $username, $password, $connect_string);
518 } elsif($arg =~ /^([^\@]+)\@(.*)$/) {
519 # username@connect_string
520 $username = $1;
521 $password = '';
522 $connect_string = $2;
523 return($ora_session_mode, $username, $password, $connect_string);
524 } elsif($arg =~ /^([^\/]+)\/([^\@]+)$/) {
525 # username/password
526 $username = $1;
527 $password = $2;
528 $connect_string = '';
529 return($ora_session_mode, $username, $password, $connect_string);
530 } elsif($arg =~ /^([^\/\@]+)$/) {
531 # username
532 $username = $1;
533 $password = $2;
534 $connect_string = '';
535 return($ora_session_mode, $username, $password, $connect_string);
536 } elsif($arg =~ /^\@(.*)$/) {
537 # @connect_string
538 $username = '';
539 $password = '';
540 $connect_string = $1;
541 return($ora_session_mode, $username, $password, $connect_string);
542 } else {
543 return(undef,undef,undef,undef);
547 sub populate_completion_list {
548 my($inline_print, $current_table_name) = @_;
549 debugmsg(3, "populate_completion_list called", @_);
551 # grab all the table and column names and put them in @completion_list
553 if($inline_print) {
554 $| = 1;
555 print STDERR "...";
556 } else {
557 print STDERR "Generating auto-complete list...\n";
560 if($conf{extended_complete_list}) {
561 my @queries;
562 if($conf{complete_tables}) {
563 push(@queries, 'select table_name from all_tables');
565 if($conf{complete_columns}) {
566 push(@queries, 'select column_name from all_tab_columns');
568 if($conf{complete_objects}) {
569 push(@queries, 'select object_name from all_objects');
572 my $sqlstr = join(' union ', @queries);
573 debugmsg(3, "query: [$sqlstr]");
575 my $sth = $dbh->prepare($sqlstr)
576 or query_err('prepare', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
577 $sth->execute()
578 or query_err('execute', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
579 while(my $res = $sth->fetchrow_array()) {
580 push(@completion_list, $res);
582 } else {
583 my @queries;
584 if($conf{complete_tables}) {
585 push(@queries, "select 'table-' || table_name from user_tables");
587 if($conf{complete_columns}) {
588 push(@queries, "select 'column-' || column_name from user_tab_columns");
590 if($conf{complete_objects}) {
591 push(@queries, "select 'object-' || object_name from user_objects");
594 my $sqlstr = join(' union ', @queries);
595 debugmsg(3, "query: [$sqlstr]");
597 my $sth = $dbh->prepare($sqlstr)
598 or query_err('prepare', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
599 $sth->execute()
600 or query_err('execute', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
601 while(my $res = $sth->fetchrow_array()) {
602 push(@completion_list, $res);
606 if ($conf{command_complete_list}) {
607 push(@completion_list, "command-create", "command-select", "command-insert", "command-update", "command-delete from", "command-from", "command-execute", "command-show", "command-describe", "command-drop");
608 push(@completion_list, "show-objects", "show-tables", "show-indexes", "show-sequences", "show-views", "show-functions", "show-constraints", "show-keys", "show-checks", "show-triggers", "show-query", "show-dimensions", "show-clusters", "show-procedures", "show-packages", "show-indextypes", "show-libraries", "show-materialized views", "show-snapshots", "show-synonyms", "show-waits", "show-processes", "show-errors", "show-user", "show-users", "show-uid", "show-plan", "show-database links", "show-dblinks");
611 if ($current_table_name) {
613 my @queries;
614 push(@queries, "select 'current_column-$current_table_name.' || column_name from user_tab_columns where table_name=\'".uc($current_table_name)."\'");
616 my $sqlstr = join(' union ', @queries);
617 debugmsg(3, "query: [$sqlstr]");
619 my $sth = $dbh->prepare($sqlstr)
620 or query_err('prepare', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
621 $sth->execute()
622 or query_err('execute', $DBI::errstr, $sqlstr), setup_sigs(), return(0);
623 while(my $res = $sth->fetchrow_array()) {
624 push(@completion_list, $res);
628 setup_sigs();
630 if($inline_print) {
631 print "\r";
632 print "\e[K";
633 $| = 0;
634 $term->forced_update_display();
638 sub complete_entry_function {
639 my($word, $state) = @_;
640 debugmsg(3, "complete_entry_function called", @_);
641 # This is called by Term::ReadLine::Gnu when a list of matches needs to
642 # be generated. It takes a string that is the word to be completed and
643 # a state number, which should increment every time it's called.
645 return unless $connected;
647 my $line_buffer = $attribs->{line_buffer};
648 debugmsg(4, "line_buffer: [$line_buffer]");
650 if($line_buffer =~ /^\s*\@/) {
651 return($term->filename_completion_function(@_));
654 unless($completion_built) {
655 unless($opt_nocomp || !$conf{auto_complete}) {
656 populate_completion_list(1);
658 $completion_built = 1;
661 if($state == 0) {
662 # compute all the possibilies and put them in @completion_possibles
663 @completion_possibles = ();
664 my $last_char = substr($word,length($word)-1,1);
666 debugmsg(2,"last_char: [$last_char]");
668 my @grep = ();
669 if ($line_buffer =~ /select(?!.*(?:from|where))[\s\w\$\#_,]*\.[\w_]*$/) {
670 # This case is for "select mytable.mycolumn" type lines
671 my $current_table_name = $line_buffer;
672 $current_table_name =~ s/(select.*)(\s)([\w_]+)(\.)([\w_]*)$/$3/;
673 debugmsg(3, "current table name: $current_table_name");
675 unless($opt_nocomp || !$conf{auto_complete}) {
676 populate_completion_list(1, $current_table_name);
679 debugmsg(4, "select table.column");
681 push(@grep, '^current_column-');
682 } elsif($line_buffer =~ /select(?!.*(?:from|where))[\s\w\$\#_,]+$/) {
683 debugmsg(4, "select ...");
684 push(@grep, '^column-', '^table-');
685 } elsif($line_buffer =~ /from(?!.*where)[\s\w\$\#_,]*$/) {
686 debugmsg(4, "from ...");
687 push(@grep, '^table-');
688 } elsif($line_buffer =~ /where[\s\w\$\#_,]*$/) {
689 debugmsg(4, "where ...");
690 push(@grep, '^column-');
691 } elsif($line_buffer =~ /update(?!.*set)[\s\w\$\#_,]*$/) {
692 debugmsg(4, "where ...");
693 push(@grep, '^table-');
694 } elsif($line_buffer =~ /set[\s\w\$\#_,]*$/) {
695 debugmsg(4, "where ...");
696 push(@grep, '^column-');
697 } elsif($line_buffer =~ /insert.*into(?!.*values)[\s\w\$\#_,]*$/) {
698 debugmsg(4, "where ...");
699 push(@grep, '^table-');
700 } elsif($line_buffer =~ /^\s*show\s\w*/) {
701 push(@grep, 'show-');
702 } else {
703 push(@grep, '');
705 debugmsg(2,"grep: [@grep]");
707 my $use_lower;
708 if($last_char =~ /^[A-Z]$/) {
709 $use_lower = 0;
710 } else {
711 $use_lower = 1;
713 foreach my $grep (@grep) {
714 foreach my $list_item (grep(/$grep/, @completion_list)) {
715 my $item = $list_item;
716 $item =~ s/^\w*-//;
717 eval { #Trap errors
718 if($item =~ /^\Q$word\E/i) {
719 push(@completion_possibles,
720 ($use_lower ? lc($item) : uc($item))
724 debugmsg(2, "Trapped error in complete_entry_function eval: $@") if $@;
727 debugmsg(3,"possibles: [@completion_possibles]");
730 # return the '$state'th element of the possibles
731 return($completion_possibles[$state] || undef);
734 sub db_reconnect {
735 debugmsg(3, "db_reconnect called", @_);
736 # This first disconnects the database, then tries to reconnect
738 print "Reconnecting...\n";
740 commit_on_exit();
742 if (defined $dbh) {
743 if (not $dbh->disconnect()) {
744 warn "Disconnect failed: $DBI::errstr\n";
745 return;
749 $dbh = db_connect(1, @dbparams);
752 sub db_connect {
753 my($die_on_error, $ora_session_mode, $username, $password, $connect_string) = @_;
754 debugmsg(3, "db_connect called", @_);
755 # Tries to connect to the database, prompting for username and password
756 # if not given. There are several cases that can happen:
757 # connect_string is present:
758 # ORACLE_HOME has to exist and the driver tries to make a connection to
759 # given connect_string.
760 # connect_string is not present:
761 # $opt_host is set:
762 # Connect to $opt_host on $opt_sid. Specify port only if $opt_port is
763 # set
764 # $opt_host is not set:
765 # Try to make connection to the default database by not specifying any
766 # host or connect string
768 my($dbhandle, $dberr, $dberrstr, $this_prompt_host, $this_prompt_user);
770 debugmsg(1,"ora_session_mode: [$ora_session_mode] username: [$username] password: [$password] connect_string: [$connect_string]");
772 # The first thing we're going to check is that the Oracle DBD is available
773 # since it's a sorta required element =)
774 my @drivers = DBI->available_drivers();
775 my $found = 0;
776 foreach(@drivers) {
777 if($_ eq "Oracle") {
778 $found = 1;
781 unless($found) {
782 lerr("Could not find DBD::Oracle... please install. Available drivers: "
783 .join(", ", @drivers) . ".\n");
785 #print "drivers: [" . join("|", @drivers) . "]\n";
787 # Now we can attempt a connection to the database
788 my $attributes = {
789 RaiseError => 0,
790 PrintError => 0,
791 AutoCommit => $conf{auto_commit},
792 LongReadLen => $conf{long_read_len},
793 LongTruncOk => $conf{long_trunc_ok},
794 ora_session_mode => $ora_session_mode
797 if($connect_string eq 'external') {
798 # the user wants to connect with external authentication
800 check_oracle_home();
802 # install alarm signal handle
803 $SIG{ALRM} = \&sighandle;
804 alarm($conf{connection_timeout});
806 if(!$opt_batch) {
807 print "Attempting connection to local database\n";
809 $dbhandle = DBI->connect('dbi:Oracle:',undef,undef,$attributes)
810 or do {
811 $dberr = $DBI::err;
812 $dberrstr = $DBI::errstr;
815 $this_prompt_host = $ENV{ORACLE_SID};
816 $this_prompt_user = $ENV{LOGNAME};
817 alarm(0); # cancel alarm
818 } elsif($connect_string) {
819 # We were provided with a connect string, so we can use the TNS method
821 check_oracle_home();
822 ($ora_session_mode, $username, $password) = get_up($ora_session_mode, $username, $password);
823 $attributes->{ora_session_mode} = $ora_session_mode if $ora_session_mode;
825 my $userstring;
826 if($username) {
827 $userstring = $username . '@' . $connect_string;
828 } else {
829 $userstring = $connect_string;
832 # install alarm signal handle
833 $SIG{ALRM} = \&sighandle;
834 alarm($conf{connection_timeout});
836 if(!$opt_batch) {
837 print "Attempting connection to $userstring\n";
839 $dbhandle = DBI->connect('dbi:Oracle:',$userstring,$password,$attributes)
840 or do {
841 $dberr = $DBI::err;
842 $dberrstr = $DBI::errstr;
845 $this_prompt_host = $connect_string;
846 $this_prompt_user = $username;
847 alarm(0); # cancel alarm
848 } elsif($opt_host) {
849 # attempt a connection to $opt_host
850 my $dsn;
851 $dsn = "host=$opt_host";
852 $dsn .= ";sid=$opt_sid" if $opt_sid;
853 $dsn .= ";port=$opt_port" if $opt_port;
855 ($ora_session_mode, $username, $password) = get_up($ora_session_mode, $username, $password);
856 $attributes->{ora_session_mode} = $ora_session_mode if $ora_session_mode;
858 # install alarm signal handle
859 $SIG{ALRM} = \&sighandle;
860 alarm($conf{connection_timeout});
862 print "Attempting connection to $opt_host\n";
863 debugmsg(1,"dsn: [$dsn]");
864 $dbhandle = DBI->connect("dbi:Oracle:$dsn",$username,$password,
865 $attributes)
866 or do {
867 $dberr = $DBI::err;
868 $dberrstr = $DBI::errstr;
871 $this_prompt_host = $opt_host;
872 $this_prompt_host = "$opt_sid!" . $this_prompt_host if $opt_sid;
873 $this_prompt_user = $username;
874 alarm(0); # cancel alarm
875 } else {
876 # attempt a connection without specifying a hostname or anything
878 check_oracle_home();
879 ($ora_session_mode, $username, $password) = get_up($ora_session_mode, $username, $password);
880 $attributes->{ora_session_mode} = $ora_session_mode if $ora_session_mode;
882 # install alarm signal handle
883 $SIG{ALRM} = \&sighandle;
884 alarm($conf{connection_timeout});
886 print "Attempting connection to local database\n";
887 $dbhandle = DBI->connect('dbi:Oracle:',$username,$password,$attributes)
888 or do {
889 $dberr = $DBI::err;
890 $dberrstr = $DBI::errstr;
893 $this_prompt_host = $ENV{ORACLE_SID};
894 $this_prompt_user = $username;
895 alarm(0); # cancel alarm
898 if($dbhandle) {
899 # Save the parameters for reconnecting
900 @dbparams = ($ora_session_mode, $username, $password, $connect_string);
902 # set the $dbuser global for use elsewhere
903 $dbuser = $username;
904 $num_connects = 0;
905 $prompt{host} = $this_prompt_host;
906 $prompt{user} = $this_prompt_user;
908 # Get the version banner
909 debugmsg(2,"Fetching version banner");
910 my $banner = $dbhandle->selectrow_array(
911 "select banner from v\$version where banner like 'Oracle%'");
912 if(!$opt_batch) {
913 if($banner) {
914 print "Connected to: $banner\n\n";
915 } else {
916 print "Connection successful!\n";
920 if($banner =~ / (\d+)\.(\d+)\.([\d\.]+)/) {
921 my ($major, $minor, $other) = ($1, $2, $3);
922 $dbversion = $major || 8;
925 # Issue a warning about autocommit. It's nice to know...
926 print STDERR "auto_commit is " . ($conf{auto_commit} ? "ON" : "OFF")
927 . ", commit_on_exit is " . ($conf{commit_on_exit} ? "ON" : "OFF")
928 . "\n" unless $opt_batch;
929 } elsif( ($dberr eq '1017' || $dberr eq '1005')
930 && ++$num_connects < $conf{max_connection_attempts}) {
931 $dberrstr =~ s/ \(DBD ERROR: OCISessionBegin\).*//;
932 print "Error: $dberrstr\n\n";
933 #@dbparams = (0,undef,undef,$connect_string);
934 $connect_string = '' if $connect_string eq 'external';
935 $dbhandle = db_connect($die_on_error,$ora_session_mode,undef,undef,$connect_string);
936 } elsif($die_on_error) {
937 lerr("Could not connect to database: $dberrstr [$dberr]");
938 } else {
939 wrn("Could not connect to database: $dberrstr [$dberr]");
940 return(0);
943 # set the NLS_DATE_FORMAT
944 if($conf{nls_date_format}) {
945 debugmsg(2, "setting NLS_DATE_FORMAT to $conf{nls_date_format}");
946 my $sqlstr = "alter session set nls_date_format = '"
947 . $conf{nls_date_format} . "'";
948 $dbhandle->do($sqlstr) or query_err('do', $DBI::errstr, $sqlstr);
951 $connected = 1;
952 return($dbhandle);
955 sub get_prompt {
956 my($prompt_string) = @_;
957 debugmsg(3, "get_prompt called", @_);
958 # This returns a prompt. It can be passed a string which will
959 # be manually put into the prompt. It will be padded on the left with
960 # white space
962 $prompt_length ||= 5; #just in case normal prompt hasn't been outputted
963 debugmsg(2, "prompt_length: [$prompt_length]");
965 if($prompt_string) {
966 my $temp_prompt = sprintf('%' . $prompt_length . 's', $prompt_string . '> ');
967 return($temp_prompt);
968 } else {
969 my $temp_prompt = $conf{prompt} . '> ';
970 my $temp_prompt_host = '@' . $prompt{host} if $prompt{host};
971 $temp_prompt =~ s/\%H/$temp_prompt_host/g;
972 $temp_prompt =~ s/\%U/$prompt{user}/g;
974 $prompt_length = length($temp_prompt);
975 return($temp_prompt);
979 sub get_up {
980 my($ora_session_mode, $username, $password) = @_;
981 debugmsg(3, "get_up called", @_);
983 if(!$opt_batch) {
985 setup_term() unless $term;
987 # Get username/password
988 unless($username) {
989 # prompt for the username
990 $username = $term->readline('Username: ');
991 if($username =~ /^(.*)\s+as\s+sys(\w+)\s*$/i) {
992 $ora_session_mode = 2 if lc($2) eq 'dba';
993 $ora_session_mode = 4 if lc($2) eq 'oper';
994 $username = $1;
997 # Take that entry off of the history list
998 if ($term_type eq 'gnu') {
999 $term->remove_history($term->where_history());
1003 unless($password) {
1004 # prompt for the password, and disable echo
1005 my $orig_redisplay = $attribs->{redisplay_function};
1006 $attribs->{redisplay_function} = \&shadow_redisplay;
1008 $password = $term->readline('Password: ');
1010 $attribs->{redisplay_function} = $orig_redisplay;
1012 # Take that entry off of the history list
1013 if ($term->ReadLine eq "Term::ReadLine::Gnu") {
1014 $term->remove_history($term->where_history());
1019 return($ora_session_mode, $username, $password);
1023 sub check_oracle_home {
1025 # This checks for the ORACLE_HOME environment variable and dies if it's
1026 # not set
1027 lerr("Please set your ORACLE_HOME environment variable!")
1028 unless $ENV{ORACLE_HOME};
1029 return(1);
1032 sub shadow_redisplay {
1033 # The one provided in Term::ReadLine::Gnu was broken
1034 # debugmsg(2, "shadow_redisplay called", @_);
1035 my $OUT = $attribs->{outstream};
1036 my $oldfh = select($OUT); $| = 1; select($oldfh);
1037 print $OUT ("\r", $attribs->{prompt});
1038 $oldfh = select($OUT); $| = 0; select($oldfh);
1041 sub print_non_print {
1042 my($string) = @_;
1044 my @string = unpack("C*", $string);
1045 my $ret_string;
1046 foreach(@string) {
1047 if($_ >= 40 && $_ <= 176) {
1048 $ret_string .= chr($_);
1049 } else {
1050 $ret_string .= "<$_>";
1053 return($ret_string);
1056 sub interface {
1057 debugmsg(3, "interface called", @_);
1058 # this is the main program loop that handles all the user input.
1059 my $input;
1060 my $prompt = get_prompt();
1062 setup_sigs();
1064 # Check if we were interactively called, or do we need to process STDIN
1065 if(-t STDIN) {
1066 while(defined($input = $term->readline($prompt))) {
1067 $sigintcaught = 0;
1068 $prompt = process_input($input, $prompt) || get_prompt();
1069 setup_sigs();
1071 } else {
1072 debugmsg(3, "non-interactive", @_);
1073 debugmsg(3, "\$opt_batch=$opt_batch", @_);
1074 debugmsg(3, "\$opt_batch=$opt_batch", @_);
1075 # Send STDIN to process_input();
1076 while(<STDIN>) {
1077 process_input($_);
1081 quit(0, undef, "\n");
1084 sub process_input {
1085 my($input, $prompt, $add_to_history) = @_;
1086 if (!(defined($add_to_history))) {
1087 $add_to_history = 1;
1089 debugmsg(3, "process_input called", @_);
1091 my $nprompt;
1092 SWITCH: {
1093 if(!$qbuffer) {
1094 # Commands that are only allowed if there is no current buffer
1095 $input =~ /^\s*(?:!|host)\s*(.*)\s*$/i and system($1), last SWITCH;
1096 $input =~ /^\s*\\a\s*$/i and populate_completion_list(), last SWITCH;
1097 $input =~ /^\s*\\\?\s*$/i and help(), last SWITCH;
1098 $input =~ /^\s*help\s*$/i and help(), last SWITCH;
1099 $input =~ /^\s*reconnect\s*$/i and db_reconnect(), last SWITCH;
1100 $input =~ /^\s*\\r\s*$/i and db_reconnect(), last SWITCH;
1101 $input =~ /^\s*conn(?:ect)?\s+(.*)$/i and connect_cmd($1), last SWITCH;
1102 $input =~ /^\s*disc(?:onnect)\s*$/i and disconnect_cmd($1), last SWITCH;
1103 $input =~ /^\s*\@\S+\s*$/i and $nprompt = run_script($input), last SWITCH;
1104 $input =~ /^\s*debug\s*(.*)$/i and debug_toggle($1), last SWITCH;
1105 $input =~ /^\s*autocommit\s*(.*)$/i and autocommit_toggle(), last SWITCH;
1106 $input =~ /^\s*commit/i and commit_cmd(), last SWITCH;
1107 $input =~ /^\s*rollback/i and rollback_cmd(), last SWITCH;
1108 $input =~ /^\s*(show\s*[^;\/\\]+)\s*$/i and show($1, 'table'),last SWITCH;
1109 $input =~ /^\s*(desc\s*[^;\/\\]+)\s*$/i and describe($1, 'table'),
1110 last SWITCH;
1111 $input =~ /^\s*(set\s*[^;\/\\]+)\s*$/i and set_cmd($1), last SWITCH;
1112 $input =~ /^\s*(let\s*[^;\/\\]*)\s*$/i and let_cmd($1), last SWITCH;
1113 $input =~ /^\s*exec(?:ute)?\s*(.*)\s*$/i and exec_cmd($1), last SWITCH;
1114 $input =~ /^\s*\\d\s*$/ and show('show objects', 'table'), last SWITCH;
1115 $input =~ /^\s*\\dt\s*$/ and show('show tables', 'table'), last SWITCH;
1116 $input =~ /^\s*\\di\s*$/ and show('show indexes', 'table'), last SWITCH;
1117 $input =~ /^\s*\\ds\s*$/ and show('show sequences', 'table'), last SWITCH;
1118 $input =~ /^\s*\\dv\s*$/ and show('show views', 'table'), last SWITCH;
1119 $input =~ /^\s*\\df\s*$/ and show('show functions', 'table'), last SWITCH;
1121 # Global commands allowed any time (even in the middle of queries)
1122 $input =~ /^\s*quit\s*$/i and quit(0), last SWITCH;
1123 $input =~ /^\s*exit\s*$/i and quit(0), last SWITCH;
1124 $input =~ /^\s*\\q\s*$/i and quit(0), last SWITCH;
1125 $input =~ /^\s*\\l\s*$/i and show_qbuffer(), last SWITCH;
1126 $input =~ /^\s*\\p\s*$/i and show_qbuffer(), last SWITCH;
1127 $input =~ /^\s*l\s*$/i and show_qbuffer(), last SWITCH;
1128 $input =~ /^\s*list\s*$/i and show_qbuffer(), last SWITCH;
1129 $input =~ /^\s*\\c\s*$/i and $nprompt = clear_qbuffer(), last SWITCH;
1130 $input =~ /^\s*clear\s*$/i and $nprompt = clear_qbuffer(), last SWITCH;
1131 $input =~ /^\s*clear buffer\s*$/i and $nprompt=clear_qbuffer(), last SWITCH;
1132 $input =~ /^\s*\\e\s*(.*)$/i and $nprompt = edit($1), last SWITCH;
1133 $input =~ /^\s*edit\s*(.*)$/i and $nprompt = edit($1), last SWITCH;
1134 $input =~ /^\s*rem(?:ark)?/i and $input = '', last SWITCH;
1135 $input =~ /[^\s]/ and $nprompt = parse_input($input) || last, last SWITCH;
1137 # default
1138 $nprompt = $prompt if ($nprompt eq ''); # use last prompt if nothing caught (blank line)
1140 if(!$opt_batch && $term->ReadLine eq "Term::ReadLine::Gnu" && $input =~ /[^\s]/ &&
1141 $input ne $last_history) {
1142 if (!$opt_batch && $add_to_history) {
1143 $term->AddHistory($input);
1146 $last_history = $input;
1147 return($nprompt);
1150 sub parse_input {
1151 my($input) = @_;
1152 debugmsg(3, "parse_input called", @_);
1153 # this takes input and parses it. It looks for single quotes (') and double
1154 # quotes (") and presents prompts accordingly. It also looks for query
1155 # terminators, such as semicolon (;), forward-slash (/) and back-slash-g (\g).
1156 # If it finds a query terminator, then it pushes any text onto the query
1157 # buffer ($qbuffer) and then passes the entire query buffer, as well as the
1158 # format type, determined by the terminator type, to the query() function. It
1159 # also wipes out the qbuffer at this time.
1161 # It returns a prompt (like 'SQL> ' or ' -> ') if successfull, 0 otherwise
1163 # now we need to check for a terminator, if we're not inquotes
1164 while( $input =~ m/
1166 ['"] # match quotes
1167 | # or
1168 ; # the ';' terminator
1169 | # or
1170 ^\s*\/\s*$ # the slash terminator at end of string
1171 | # or
1172 \\[GgsSi] # one of the complex terminators
1173 | # or
1174 (?:^|\s+)create\s+ # create
1175 | # or
1176 (?:^|\s+)function\s+ # function
1177 | # or
1178 (?:^|\s+)package\s+ # package
1179 | # or
1180 (?:^|\s+)package\s+body\s+ # package body
1181 | # or
1182 (?:^|\s+)procedure\s+ # procedure
1183 | # or
1184 (?:^|\s+)trigger\s+ # trigger
1185 | # or
1186 (?:^|\s+)declare\s+ # declare
1187 | # or
1188 (?:^|\s+)begin\s+ # begin
1189 | # or
1190 \/\* # start of multiline comment
1191 | # or
1192 \*\/ # end of multiline comment
1193 )/gix )
1196 my($pre, $match, $post) = ($`, $1, $');
1197 # PREMATCH, MATCH, POSTMATCH
1198 debugmsg(1, "parse: [$pre] [$match] [$post]");
1200 if( ($match eq '\'' || $match eq '"')) {
1201 if(!$quote || $quote eq $match) {
1202 $inquotes = ($inquotes ? 0 : 1);
1203 if($inquotes) {
1204 $quote = $match;
1205 } else {
1206 undef($quote);
1209 } elsif($match =~ /create/ix) {
1210 $increate = 1;
1211 } elsif(!$increate &&
1212 $match =~ /function|package|package\s+body|procedure|trigger/ix)
1214 # do nothing if we're not in a create statement
1215 } elsif(($match =~ /declare|begin/ix) ||
1216 ($increate && $match =~ /function|package|package\s+body|procedure|trigger/ix))
1218 $inplsqlblock = 1;
1219 } elsif($match =~ /^\/\*/) {
1220 $incomment = 1;
1221 } elsif($match =~ /^\*\//) {
1222 $incomment = 0;
1223 } elsif(!$inquotes && !$incomment && $match !~ /^--/ &&
1224 ($match =~ /^\s*\/\s*$/ || !$inplsqlblock))
1226 $qbuffer .= $pre;
1227 debugmsg(4,"qbuffer IN: [$qbuffer]");
1228 my $terminator = $match;
1229 $post =~ / (\d*) # Match num_rows right after terminitor
1230 \s* # Optional whitespace
1231 (?: #
1232 ( >{1,2}|<|\| ) # Match redirection operators
1233 \s* # Optional whitespace
1234 ( .* ) # The redirector (include rest of line)
1235 )? # Match 0 or 1
1236 \s* # Optional whitespace
1237 (.*) # Catch everything else
1238 $ # End-Of-Line
1240 debugmsg(3,"1: [$1] 2: [$2] 3: [$3] 4: [$4]");
1242 my($num_rows,$op,$op_text,$extra) = ($1,$2,$3,$4);
1244 if($extra =~ /--.*$/) {
1245 undef $extra;
1248 # check that Text::CSV_XS is installed if a < redirection was given
1249 if($op eq '<' && $notextcsv) {
1250 soft_err("You must install Text::CSV_XS from CPAN to use this feature");
1251 return(0);
1254 # deduce the format from the terminator type
1255 my $format;
1257 $fbuffer = $terminator;
1259 if($terminator eq ';' || $terminator =~ /^\/\s*$/) {
1260 $format = 'table';
1261 } elsif($terminator eq '\g') {
1262 $format = 'list';
1263 } elsif($terminator eq '\G') {
1264 $format = 'list_aligned';
1265 } elsif($terminator eq '\s') {
1266 $format = 'csv';
1267 } elsif($terminator eq '\S') {
1268 $format = 'csv_no_header';
1269 } elsif($terminator eq '\i') {
1270 $format = 'sql';
1272 $num_rows ||= 0;
1274 debugmsg(4,"fbuffer: [$fbuffer]\n");
1276 # if there is nothing in the buffer, then we assume that the user just
1277 # wants to reexecute the last query, which we have saved in $last_qbuffer
1278 my($use_buffer, $copy_buffer);
1279 if($qbuffer) {
1280 $use_buffer = $qbuffer;
1281 $copy_buffer = 1;
1282 } elsif($last_qbuffer) {
1283 $use_buffer = $last_qbuffer;
1284 $copy_buffer = 0;
1285 } else {
1286 $use_buffer = undef;
1287 $copy_buffer = 0;
1290 if($use_buffer) {
1291 if($op eq '<') {
1292 my $count = 0;
1293 my($max_lines, @params, $max_lines_save, @querybench,
1294 $rows_affected, $success_code);
1295 my $result_output = 1;
1296 push(@querybench, get_bench());
1297 print STDERR "\n";
1298 while(($max_lines, @params) = get_csv_file($op, $op_text)) {
1299 $max_lines_save = $max_lines;
1300 print statusline($count, $max_lines);
1302 my @res = query( $use_buffer, $format,
1303 {num_rows => $num_rows, op => $op, op_text => $op_text,
1304 result_output => 0}, @params);
1306 debugmsg(3, "res: [@res]");
1308 unless(@res) {
1309 print "Error in line " . ($count + 1) . " of file '$op_text'\n";
1310 $result_output = 0;
1311 close_csv();
1312 last;
1315 $rows_affected += $res[0];
1316 $success_code = $res[1];
1317 $count++;
1319 push(@querybench, get_bench());
1321 if($result_output) {
1322 print "\r\e[K";
1324 if(!$opt_batch) {
1325 print STDERR format_affected($rows_affected, $success_code);
1326 if($opt_bench || $conf{extended_benchmarks}) {
1327 print STDERR "\n\n";
1328 print STDERR ('-' x 80);
1329 print STDERR "\n";
1330 output_benchmark("Query: ", @querybench, "\n");
1331 } else {
1332 output_benchmark(" (", @querybench, ")");
1333 print STDERR "\n";
1335 print STDERR "\n";
1338 } else {
1339 query($use_buffer, $format, {num_rows => $num_rows, op => $op,
1340 op_text => $op_text});
1343 if($copy_buffer) {
1344 # copy the current qbuffer to old_qbuffer
1345 $last_qbuffer = $qbuffer;
1346 $last_fbuffer = $fbuffer;
1348 } else {
1349 query_err('Query', 'No current query in buffer');
1352 undef($qbuffer);
1353 undef($fbuffer);
1354 $inplsqlblock = 0;
1355 $increate = 0;
1357 if($extra) {
1358 return(parse_input($extra));
1359 } else {
1360 # return a 'new' prompt
1361 return(get_prompt());
1366 $qbuffer .= $input . "\n";
1368 debugmsg(4,"qbuffer: [$qbuffer], input: [$input]");
1370 if($inquotes) {
1371 return(get_prompt($quote));
1372 } elsif($incomment) {
1373 return(get_prompt('DOC'));
1374 } else {
1375 return(get_prompt('-'));
1379 sub get_csv_file {
1380 my($op, $op_text) = @_;
1381 debugmsg(3, "get_csv_file called", @_);
1383 my @ret = ();
1385 unless($csv_max_lines) {
1386 ($op_text) = glob($op_text);
1387 debugmsg(3, "Opening file '$op_text' for line counting");
1388 open(CSV, $op_text) || do{
1389 query_err('redirect',"Cannot open file '$op_text' for reading: $!");
1390 return();
1392 while(<CSV>) {
1393 $csv_max_lines++;
1395 close(CSV);
1398 unless($csv_filehandle_open) {
1399 ($op_text) = glob($op_text);
1400 debugmsg(3, "Opening file '$op_text' for input");
1401 open(CSV, $op_text) || do{
1402 query_err('redirect',"Cannot open file '$op_text' for reading: $!");
1403 return();
1405 $csv_filehandle_open = 1;
1408 my $line = <CSV>;
1409 while(defined($line) && $line =~ /^\s*$/) {
1410 $line = <CSV>;
1413 unless($line) {
1414 close_csv();
1415 return();
1418 debugmsg(3, "read in CSV line", $line);
1420 my @fields;
1421 if($csv->parse($line)) {
1422 @fields = $csv->fields();
1423 debugmsg(3, "got CVS fields", @fields);
1424 } else {
1425 wrn("Parse of CSV file failed on argument, skipping to next: "
1426 . $csv->error_input());
1427 return(get_csv_file($op, $op_text));
1430 return($csv_max_lines, @fields);
1433 sub close_csv {
1434 close(CSV) || lerr("Could not close CSV filehandle: $!");
1435 $csv_filehandle_open = 0;
1436 $csv_max_lines = 0;
1439 sub connect_cmd {
1440 my($arg) = @_;
1441 debugmsg(3, "connect_cmd called", @_);
1443 unless($arg) {
1444 wrn("Invalid connect syntax. See help");
1445 return(0);
1448 my($ora_session_mode, $username, $password, $connect_string) = parse_logon_string($arg);
1450 my $new_dbh = db_connect(0, $ora_session_mode, $username, $password, $connect_string);
1451 if (not $new_dbh) {
1452 warn "failed to make new connection as $username to $connect_string: $DBI::errstr\n";
1453 warn "keeping old connection\n";
1454 return;
1457 if (defined $dbh) {
1458 commit_on_exit();
1459 $dbh->disconnect()
1460 or warn "failed to disconnect old connection - switching anyway\n";
1463 $dbh = $new_dbh;
1464 $connected = 1;
1467 sub disconnect_cmd {
1468 debugmsg(3, "disconnect_cmd called", @_);
1470 if ($connected) {
1471 print "Closing last connection...\n";
1472 commit_on_exit();
1474 $dbh->disconnect() if (defined $dbh);
1475 $connected = 0;
1476 } else {
1477 print "Not connected.\n";
1481 sub commit_cmd {
1482 debugmsg(3, "commit_cmd called", @_);
1483 # this just called commit
1485 if(defined $dbh) {
1486 if($dbh->{AutoCommit}) {
1487 wrn("commit ineffective with AutoCommit enabled");
1488 } else {
1489 if ($dbh->commit()) {
1490 print "Transaction committed\n";
1492 else {
1493 warn "Commit failed: $DBI::errstr\n";
1496 } else {
1497 print "No connection\n";
1501 sub rollback_cmd {
1502 debugmsg(3, "rollback_cmd called", @_);
1503 # this just called commit
1505 if(defined $dbh) {
1506 if($dbh->{AutoCommit}) {
1507 wrn("rollback ineffective with AutoCommit enabled");
1508 } else {
1509 if ($dbh->rollback()) {
1510 print "Transaction rolled back\n";
1512 else {
1513 warn "Rollback failed: $DBI::errstr\n";
1516 } else {
1517 print "No connection\n";
1521 sub exec_cmd {
1522 my($sqlstr) = @_;
1523 debugmsg(3, "exec_cmd called", @_);
1524 # Wrap the statement in BEGIN/END and execute
1526 $sqlstr = qq(
1527 BEGIN
1528 $sqlstr
1529 END;
1532 query($sqlstr, 'table');
1535 sub edit {
1536 my($filename) = @_;
1537 debugmsg(3, "edit called", @_);
1538 # This writes the current qbuffer to a file then opens up an editor on that
1539 # file... when the editor returns, we read in the file and overwrite the
1540 # qbuffer with it. If there is nothing in the qbuffer, and there is
1541 # something in the last_qbuffer, then we use the last_qbuffer. If nothing
1542 # is in either, then we just open the editor with a blank file.
1544 my $passed_file = 1 if $filename;
1545 my $filecontents;
1546 my $prompt = get_prompt();
1548 debugmsg(2, "passed_file: [$passed_file]");
1550 if($qbuffer) {
1551 debugmsg(2, "Using current qbuffer for contents");
1552 $filecontents = $qbuffer;
1553 } elsif($last_qbuffer) {
1554 debugmsg(2, "Using last_qbuffer for contents");
1555 $filecontents = $last_qbuffer . $last_fbuffer;
1556 } else {
1557 debugmsg(2, "Using blank contents");
1558 $filecontents = "";
1561 debugmsg(3, "filecontents: [$filecontents]");
1563 # determine the tmp directory
1564 my $tmpdir;
1565 if($ENV{TMP}) {
1566 $tmpdir = $ENV{TMP};
1567 } elsif($ENV{TEMP}) {
1568 $tmpdir = $ENV{TEMP};
1569 } elsif(-d "/tmp") {
1570 $tmpdir = "/tmp";
1571 } else {
1572 $tmpdir = ".";
1575 # determine the preferred editor
1576 my $editor;
1577 if($ENV{EDITOR}) {
1578 $editor = $ENV{EDITOR};
1579 } else {
1580 $editor = "vi";
1583 # create the filename, if not given one
1584 $filename ||= "$tmpdir/yasql_" . int(rand(1000)) . "_$$.sql";
1586 # expand the filename
1587 ($filename) = glob($filename);
1589 debugmsg(1, "Editing $filename with $editor");
1591 # check for file existance. If it exists, then we open it up but don't
1592 # write the buffer to it
1593 my $file_exists;
1594 if($passed_file) {
1595 # if the file was passed, then check for it's existance
1596 if(-e $filename) {
1597 # The file was found
1598 $file_exists = 1;
1599 } elsif(-e "$filename.sql") {
1600 # the file was found with a .sql extension
1601 $filename = "$filename.sql";
1602 $file_exists = 1;
1603 } else {
1604 wrn("$filename was not found, creating new file, which will not be ".
1605 "deleted");
1607 } else {
1608 # no file was specified, so just write to the the temp file, and we
1609 # don't care if it exists, since there's no way another process could
1610 # write to the same file at the same time since we use the PID in the
1611 # filename.
1612 my $ret = open(TMPFILE, ">$filename");
1613 if(!$ret) { #if file was NOT opened successfully
1614 wrn("Could not write to $filename: $!");
1615 } else {
1616 print TMPFILE $filecontents;
1617 close(TMPFILE);
1621 # now spawn the editor
1622 my($ret, @filecontents);
1623 debugmsg(2, "Executing $editor $filename");
1624 $ret = system($editor, "$filename");
1625 if($ret) {
1626 debugmsg(2, "Executing env $editor $filename");
1627 $ret = system("env", $editor, "$filename");
1629 if($ret) {
1630 debugmsg(2, "Executing `which $editor` $filename");
1631 $ret = system("`which $editor`", "$filename");
1634 if($ret) { #if the editor or system returned a positive return value
1635 wrn("Editor exited with $ret: $!");
1636 } else {
1637 # read in the tmp file and apply it's contents to the buffer
1638 my $ret = open(TMPFILE, "$filename");
1639 if(!$ret) { # if file was NOT opened successfully
1640 wrn("Could not read $filename: $!");
1641 } else {
1642 # delete our qbuffer and reset the inquotes var
1643 $qbuffer = "";
1644 $inquotes = 0;
1645 $increate = 0;
1646 $inplsqlblock = 0;
1647 $incomment = 0;
1648 while(<TMPFILE>) {
1649 push(@filecontents, $_);
1651 close(TMPFILE);
1655 if(@filecontents) {
1656 print "\n";
1657 print join('', @filecontents);
1658 print "\n";
1660 foreach my $line (@filecontents) {
1661 # chomp off newlines
1662 chomp($line);
1664 last if $sigintcaught;
1665 # now send it in to process_input
1666 # and don't add lines of the script to command history
1667 $prompt = process_input($line, '', 0);
1671 unless($passed_file) {
1672 # delete the tmp file
1673 debugmsg(1, "Deleting $filename");
1674 unlink("$filename") ||
1675 wrn("Could not unlink $filename: $!");
1678 return($prompt);
1681 sub run_script {
1682 my($input) = @_;
1683 debugmsg(3, "run_script called", @_);
1684 # This reads in the given script and executes it's lines as if they were typed
1685 # in directly. It will NOT erase the current buffer before it runs. It
1686 # will append the contents of the file to the current buffer, basicly
1688 my $prompt;
1690 # parse input
1691 $input =~ /^\@(.*)$/;
1692 my $file = $1;
1693 ($file) = glob($file);
1694 debugmsg(2, "globbed [$file]");
1696 my $first_char = substr($file, 0, 1);
1697 unless($first_char eq '/' or $first_char eq '.') {
1698 foreach my $path ('.', @sqlpath) {
1699 if(-e "$path/$file") {
1700 $file = "$path/$file";
1701 last;
1702 } elsif(-e "$path/$file.sql") {
1703 $file = "$path/$file.sql";
1704 last;
1708 debugmsg(2, "Found [$file]");
1710 # read in the tmp file and apply it's contents to the buffer
1711 my $ret = open(SCRIPT, $file);
1712 if(!$ret) { # if file was NOT opened successfully
1713 wrn("Could not read $file: $!");
1714 $prompt = get_prompt();
1715 } else {
1716 # read in the script
1717 while(<SCRIPT>) {
1718 # chomp off newlines
1719 chomp;
1721 last if $sigintcaught;
1723 # now send it in to process_input
1724 # and don't add lines of the script to command history
1725 $prompt = process_input($_, '', 0);
1727 close(SCRIPT);
1730 return($prompt);
1733 sub show_qbuffer {
1734 debugmsg(3, "show_qbuffer called", @_);
1735 # This outputs the current buffer
1737 #print "\nBuffer:\n";
1738 if($qbuffer) {
1739 print $qbuffer;
1740 } else {
1741 print STDERR "Buffer empty";
1743 print "\n";
1746 sub clear_qbuffer {
1747 debugmsg(3, "clear_qbuffer called", @_);
1748 # This clears the current buffer
1750 $qbuffer = '';
1751 $inquotes = 0;
1752 $inplsqlblock = 0;
1753 $increate = 0;
1754 $incomment = 0;
1755 print "Buffer cleared\n";
1756 return(get_prompt());
1759 sub debug_toggle {
1760 my($debuglevel) = @_;
1761 debugmsg(3, "debug_toggle called", @_);
1762 # If nothing is passed, then debugging is turned off if on, on if off. If
1763 # a number is passed, then we explicitly set debugging to that number
1766 if(length($debuglevel) > 0) {
1767 unless($debuglevel =~ /^\d+$/) {
1768 wrn('Debug level must be an integer');
1769 return(1);
1772 $opt_debug = $debuglevel;
1773 } else {
1774 if($opt_debug) {
1775 $opt_debug = 0;
1776 } else {
1777 $opt_debug = 1;
1780 $opt_debug > 3 ? DBI->trace(1) : DBI->trace(0);
1781 print "** debug is now " . ($opt_debug ? "level $opt_debug" : 'off') . "\n";
1784 sub autocommit_toggle {
1785 debugmsg(3, "autocommit_toggle called", @_);
1786 # autocommit is turned off if on on if off
1788 if($dbh->{AutoCommit}) {
1789 $dbh->{AutoCommit} = 0;
1790 } else {
1791 $dbh->{AutoCommit} = 1;
1794 print "AutoCommit is now " . ($dbh->{AutoCommit} ? 'on' : 'off') . "\n";
1797 sub show_all_query {
1798 my ( $select, $order_by, $format, $opts, $static_where , $option, $option_key, @values ) = @_;
1799 debugmsg(3, "show_all_query called");
1800 my $where = ' where ';
1801 if ( $static_where ) {
1802 $where = ' where '. $static_where . ' ';
1805 if ( $option eq 'like' ){
1806 my $sqlstr = $select . $where;
1807 $sqlstr .= ' and ' if ( $static_where );
1808 $sqlstr .= $option_key ." like ? " . $order_by;
1810 query($sqlstr , $format, $opts, @values );
1811 }else{
1812 my $sqlstr = $select;
1813 $sqlstr .= $where if ($static_where);
1814 $sqlstr .= $order_by;
1816 query($sqlstr , $format, $opts );
1821 sub show {
1822 my($input, $format, $num_rows, $op, $op_text) = @_;
1823 debugmsg(3, "show called", @_);
1824 # Can 'show thing'. Possible things:
1825 # tables - outputs all of the tables that the current user owns
1826 # sequences - outputs all of the sequences that the current user owns
1828 # Can also 'show thing on table'. Possible things:
1829 # constraints - Shows constraints on the 'table', like Check, Primary Key,
1830 # Unique, and Foreign Key
1831 # indexes - Shows indexes on the 'table'
1832 # triggers - Shows triggers on the 'table'
1834 # convert to lowercase for comparison operations
1835 $input = lc($input);
1837 # drop trailing whitespaces
1838 ($input = $input) =~ s/( +)$//;
1840 # parse the input to find out what 'thing' has been requested
1841 if($input =~ /^\s*show\s+([a-zA-Z0-9_\$\#\s]+)\s+(?:on|for)\s+([a-zA-Z0-9_\$\#]+)/) {
1842 # this is a thing on a table
1843 if($1 eq 'indexes') {
1844 my $sqlstr;
1845 if($dbversion >= 8) {
1846 $sqlstr = q{
1847 select ai.index_name "Index Name",
1848 ai.index_type "Type",
1849 ai.uniqueness "Unique?",
1850 aic.column_name "Column Name"
1851 from all_indexes ai, all_ind_columns aic
1852 where ai.index_name = aic.index_name
1853 and ai.table_owner = aic.table_owner
1854 and ai.table_name = ?
1855 and ai.table_owner = ?
1856 order by ai.index_name, aic.column_position
1858 } else {
1859 $sqlstr = q{
1860 select ai.index_name "Index Name",
1861 ai.uniqueness "Unique?",
1862 aic.column_name "Column Name"
1863 from all_indexes ai, all_ind_columns aic
1864 where ai.index_name = aic.index_name
1865 and ai.table_owner = aic.table_owner
1866 and ai.table_name = ?
1867 and ai.table_owner = ?
1868 order by ai.index_name, aic.column_position
1871 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1872 op_text => $op_text}, uc($2), uc($dbuser));
1873 } elsif($1 eq 'constraints') {
1874 my $sqlstr = q{
1875 select constraint_name "Constraint Name",
1876 decode(constraint_type,
1877 'C', 'Check',
1878 'P', 'Primary Key',
1879 'R', 'Foreign Key',
1880 'U', 'Unique',
1881 '') "Type",
1882 search_condition "Search Condition"
1883 from all_constraints
1884 where table_name = ?
1885 and owner = ?
1886 order by constraint_name
1888 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1889 op_text => $op_text}, uc($2), uc($dbuser));
1890 } elsif($1 eq 'keys') {
1891 my $sqlstr = q{
1892 select ac.constraint_name "Name",
1893 decode(ac.constraint_type,
1894 'R', 'Foreign Key',
1895 'U', 'Unique',
1896 'P', 'Primary Key',
1897 ac.constraint_type) "Type",
1898 ac.table_name "Table Name",
1899 acc.column_name "Column",
1900 r_ac.table_name "Parent Table",
1901 r_acc.column_name "Parent Column"
1902 from all_constraints ac, all_cons_columns acc,
1903 all_constraints r_ac, all_cons_columns r_acc
1904 where ac.constraint_name = acc.constraint_name
1905 and ac.owner = acc.owner
1906 and ac.constraint_type in ('R','U','P')
1907 and ac.r_constraint_name = r_ac.constraint_name(+)
1908 and r_ac.constraint_name = r_acc.constraint_name(+)
1909 and r_ac.owner = r_acc.owner(+)
1910 and ac.table_name = ?
1911 and ac.owner = ?
1912 order by ac.constraint_name, acc.position
1914 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1915 op_text => $op_text}, uc($2), uc($dbuser));
1916 } elsif($1 eq 'checks') {
1917 my $sqlstr = q{
1918 select ac.constraint_name "Name",
1919 decode(ac.constraint_type,
1920 'C', 'Check',
1921 ac.constraint_type) "Type",
1922 ac.table_name "Table Name",
1923 ac.search_condition "Search Condition"
1924 from all_constraints ac
1925 where ac.table_name = ?
1926 and ac.constraint_type = 'C'
1927 and ac.owner = ?
1928 order by ac.constraint_name
1930 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1931 op_text => $op_text}, uc($2), uc($dbuser));
1932 } elsif($1 eq 'triggers') {
1933 my $sqlstr = q{
1934 select trigger_name "Trigger Name",
1935 trigger_type "Type",
1936 when_clause "When",
1937 triggering_event "Event"
1938 from all_triggers
1939 where table_name = ?
1940 and owner = ?
1942 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
1943 op_text => $op_text}, uc($2), uc($dbuser));
1944 } elsif($1 eq 'query') {
1945 my $sqlstr = q{
1946 select count(*) from all_mviews where mview_name = ? and owner = ?
1948 my $is_mview = $dbh->selectrow_array($sqlstr, undef, uc($2), uc($dbuser));
1949 if($is_mview) {
1950 $sqlstr = q{
1951 select query
1952 from all_mviews
1953 where mview_name = ?
1954 and owner = ?
1956 } else {
1957 $sqlstr = q{
1958 select text
1959 from all_views
1960 where view_name = ?
1961 and owner = ?
1964 my $prev_LongReadLen = $dbh->{LongReadLen};
1965 $dbh->{LongReadLen} = 8000;
1966 query($sqlstr, 'single_output', {num_rows => $num_rows, op => $op,
1967 op_text => $op_text}, uc($2), uc($dbuser));
1968 $dbh->{LongReadLen} = $prev_LongReadLen;
1969 } elsif($1 eq 'deps') {
1970 my $table = $2;
1971 my $sqlstr = q{
1972 select
1973 column_name "Column Name"
1974 ,parent_table "Parent Table"
1975 ,parent_pk "Parent Primary Key"
1976 ,child_table "Child Table"
1977 ,child_pk "Child Primary Key"
1978 from (
1979 select
1980 a.owner,
1981 a.table_name,
1982 b.column_name,
1983 c.owner || '.' || c.table_name parent_table,
1984 d.column_name parent_pk,
1985 null child_table,
1986 null child_pk
1987 from all_constraints a,
1988 all_cons_columns b,
1989 all_constraints c,
1990 all_cons_columns d
1991 where a.constraint_name = b.constraint_name
1992 and a.r_constraint_name is not null
1993 and a.r_constraint_name=c.constraint_name
1994 and c.constraint_name=d.constraint_name
1995 and a.owner = b.owner and c.owner = d.owner
1996 UNION
1997 SELECT
1998 a.owner,
1999 a.table_name parent_table,
2000 b.column_name,
2001 null as parent_table,
2002 null as parent_pk,
2003 c.owner || '.' || c.table_name child_table, --table_name,
2004 d.column_name child_pk --child_pk,
2005 FROM all_constraints a,
2006 all_cons_columns b,
2007 all_constraints c,
2008 all_cons_columns d
2009 WHERE a.constraint_name = b.constraint_name
2010 AND a.constraint_name = c.r_constraint_name
2011 AND c.constraint_name = d.constraint_name
2012 and a.owner = b.owner and c.owner = d.owner
2013 ) where table_name like ?
2014 and owner like ?
2015 ORDER BY 1,2,3,4,5
2017 query($sqlstr, 'table', {num_rows => $num_rows, op => $op,
2018 op_text => $op_text}, uc($table), uc($dbuser));
2019 } elsif($1 eq 'ddl') {
2020 my $table = $2;
2021 my $sqlstr = q{
2022 SELECT DBMS_METADATA.GET_DDL('TABLE', ?, ?) FROM dual
2023 union all
2024 SELECT DBMS_METADATA.GET_DEPENDENT_DDL('INDEX', ?, ?) FROM dual
2025 union all
2026 SELECT DBMS_METADATA.GET_DEPENDENT_DDL ('COMMENT', ?, ?) FROM dual
2027 union all
2028 SELECT DBMS_METADATA.GET_DEPENDENT_DDL('TRIGGER', ?, ?) FROM dual
2030 my $prev_LongReadLen = $dbh->{LongReadLen};
2031 $dbh->{LongReadLen} = 16_000;
2032 query($sqlstr, 'quiet-list', {num_rows => $num_rows, op => $op, op_text => $op_text}
2033 ,uc($table)
2034 ,uc($dbuser)
2035 ,uc($table)
2036 ,uc($dbuser)
2037 ,uc($table)
2038 ,uc($dbuser)
2039 ,uc($table)
2040 ,uc($dbuser)
2042 $dbh->{LongReadLen} = $prev_LongReadLen;
2043 } else {
2044 query_err("show", "Unsupported show type", $input);
2046 } elsif($input =~ /^\s*show\s+all\s+([a-zA-Z0-9_\$\#]+)\s*([a-zA-Z0-9_\'\$\#\%\s]*)$/) {
2047 my $object = $1;
2048 my $rest = $2;
2049 my $option = '';
2050 my $option_value = '';
2051 my $opts = {
2052 num_rows => $num_rows
2053 ,op => $op
2054 ,op_text => $op_text
2056 # Workaround for materialized views
2057 if ($object eq 'materialized' and $2 =~ /views\s*([a-zA-Z0-9_\$\#\%\s]*)/ ){
2058 $object = 'materialized views';
2059 $rest = $1;
2062 if ($rest =~ /\s*(\w+)\s+[']?([a-zA-Z0-9_\$\#\%]+)[']?/){
2063 $option = lc($1);
2064 $option_value = uc($2);
2067 if($object eq 'tables') {
2069 show_all_query(
2070 q{select table_name "Table Name", 'TABLE' "Type", owner "Owner" from all_tables }
2071 ,q{ order by table_name }
2072 ,$format
2073 ,$opts
2074 ,q{}
2075 ,$option
2076 ,q{table_name}
2077 ,$option_value
2080 } elsif($object eq 'views') {
2082 show_all_query(
2083 q{select view_name "View Name", 'VIEW' "Type", owner "Owner" from all_views }
2084 ,q{ order by view_name }
2085 ,$format
2086 ,$opts
2087 ,q{}
2088 ,$option
2089 ,q{view_name}
2090 ,$option_value
2093 } elsif($object eq 'objects') {
2095 show_all_query(
2096 q{select object_name "Object Name", object_type "Type", owner "Owner" from all_objects }
2097 ,q{ order by object_name }
2098 ,$format
2099 ,$opts
2100 ,q{}
2101 ,$option
2102 ,q{object_name}
2103 ,$option_value
2106 } elsif($object eq 'sequences') {
2108 show_all_query(
2109 q{select sequence_name "Sequence Name", 'SEQUENCE' "Type", sequence_owner "Owner" from all_sequences }
2110 ,q{ order by sequence_name }
2111 ,$format
2112 ,$opts
2113 ,q{}
2114 ,$option
2115 ,q{sequence_name}
2116 ,$option_value
2119 } elsif($object eq 'clusters') {
2121 show_all_query(
2122 q{select cluster_name "Cluster Name", 'CLUSTER' "Type", owner "Owner" from all_clusters}
2123 ,q{ order by cluster_name }
2124 ,$format
2125 ,$opts
2126 ,q{}
2127 ,$option
2128 ,q{cluster_name}
2129 ,$option_value
2132 } elsif($object eq 'dimensions') {
2134 show_all_query(
2135 q{select dimension_name "Dimension Name", 'DIMENSION' "Type", owner "Owner" from all_dimensions}
2136 ,q{ order by dimension_name }
2137 ,$format
2138 ,$opts
2139 ,q{}
2140 ,$option
2141 ,q{dimension_name}
2142 ,$option_value
2145 } elsif($object eq 'functions') {
2147 show_all_query(
2148 q{select distinct name "Function Name", 'FUNCTION' "Type", owner "Owner" from all_source}
2149 ,q{ order by name }
2150 ,$format
2151 ,$opts
2152 ,q{type = 'FUNCTION'}
2153 ,$option
2154 ,q{name}
2155 ,$option_value
2158 } elsif($object eq 'procedures') {
2160 show_all_query(
2161 q{select distinct name "Procedure Name", 'PROCEDURE' "Type", owner "Owner" from all_source}
2162 ,q{ order by name }
2163 ,$format
2164 ,$opts
2165 ,q{type = 'PROCEDURE'}
2166 ,$option
2167 ,q{name}
2168 ,$option_value
2171 } elsif($object eq 'packages') {
2173 show_all_query(
2174 q{select distinct name "Package Name", 'PACKAGES' "Type", owner "Owner" from all_source}
2175 ,q{ order by name }
2176 ,$format
2177 ,$opts
2178 ,q{type = 'PACKAGE'}
2179 ,$option
2180 ,q{name}
2181 ,$option_value
2184 } elsif($object eq 'indexes') {
2186 show_all_query(
2187 q{select index_name "Index Name", 'INDEXES' "Type", owner "Owner" from all_indexes}
2188 ,q{ order by index_name }
2189 ,$format
2190 ,$opts
2191 ,q{}
2192 ,$option
2193 ,q{index_name}
2194 ,$option_value
2197 } elsif($object eq 'indextypes') {
2199 show_all_query(
2200 q{select indextype_name "Indextype Name", 'INDEXTYPE' "Type", owner "Owner" from all_indextypes}
2201 ,q{ order by indextype_name }
2202 ,$format
2203 ,$opts
2204 ,q{}
2205 ,$option
2206 ,q{indextype_name}
2207 ,$option_value
2210 } elsif($object eq 'libraries') {
2212 show_all_query(
2213 q{select library_name "library Name", 'LIBRARY' "Type", owner "Owner" from all_libraries}
2214 ,q{ order by library_name }
2215 ,$format
2216 ,$opts
2217 ,q{}
2218 ,$option
2219 ,q{library_name}
2220 ,$option_value
2223 } elsif($object eq 'materialized views') {
2225 show_all_query(
2226 q{select mview_name "Materialized View Name", 'MATERIALIZED VIEW' "Type", owner "Owner" from all_mviews}
2227 ,q{ order by mview_name }
2228 ,$format
2229 ,$opts
2230 ,q{}
2231 ,$option
2232 ,q{mview_name}
2233 ,$option_value
2236 } elsif($object eq 'snapshots') {
2238 show_all_query(
2239 q{select name "Snapshot Name", 'SNAPSHOT' "Type", owner "Owner" from all_snapshots}
2240 ,q{ order by name }
2241 ,$format
2242 ,$opts
2243 ,q{}
2244 ,$option
2245 ,q{name}
2246 ,$option_value
2249 } elsif($object eq 'synonyms') {
2251 show_all_query(
2252 q{select synonym_name "Synonym Name", 'SYNONYM' "Type", owner "Owner" from all_synonyms}
2253 ,q{ order by synonym_name }
2254 ,$format
2255 ,$opts
2256 ,q{}
2257 ,$option
2258 ,q{synonym_name}
2259 ,$option_value
2263 } elsif($object eq 'triggers') {
2265 show_all_query(
2266 q{select trigger_name "Trigger Name", 'TRIGGER' "Type", owner "Owner" from all_triggers}
2267 ,q{ order by trigger_name }
2268 ,$format
2269 ,$opts
2270 ,q{}
2271 ,$option
2272 ,q{trigger_name}
2273 ,$option_value
2276 } elsif($object eq 'waits') {
2277 my $sqlstr = q{
2278 select vs.username "Username",
2279 vs.osuser "OS User",
2280 vsw.sid "SID",
2281 vsw.event "Event",
2282 decode(vsw.wait_time, -2, ' Unknown',
2283 to_char(vsw.seconds_in_wait,'999,999,999,999'))
2284 "Seconds Waiting"
2285 from v$session_wait vsw,
2286 v$session vs
2287 where vsw.sid = vs.sid
2288 order by vsw.wait_time desc, vsw.seconds_in_wait desc, vsw.sid
2290 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2291 op_text => $op_text});
2293 } elsif( $object eq 'constraints' ){
2295 my $sqlstr = q{
2296 select
2297 CONSTRAINT_NAME "Constraint Name"
2298 ,decode(constraint_type,
2299 'C', 'Check',
2300 'P', 'Primary Key',
2301 'R', 'Foreign Key',
2302 'U', 'Unique',
2303 '') "Type"
2304 ,TABLE_NAME "Table Name"
2305 ,INDEX_NAME "Index Name"
2306 ,STATUS "Status"
2307 from all_constraints
2309 show_all_query(
2310 $sqlstr
2311 ,q{ order by CONSTRAINT_NAME }
2312 ,$format
2313 ,$opts
2314 ,q{}
2315 ,$option
2316 ,q{CONSTRAINT_NAME}
2317 ,$option_value
2320 } else {
2321 query_err("show", "Unsupported show type", $input);
2323 } elsif($input =~ /^\s*show\s+([a-zA-Z0-9_\$\#\s]+)\s*$/) {
2324 if($1 eq 'tables') {
2325 my $sqlstr = q{
2326 select table_name "Table Name", 'TABLE' "Type", sys.login_user() "Owner"
2327 from user_tables
2328 order by table_name
2330 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2331 op_text => $op_text});
2332 } elsif($1 eq 'views') {
2333 my $sqlstr = q{
2334 select view_name "View Name", 'VIEW' "Type", sys.login_user() "Owner"
2335 from user_views
2336 order by view_name
2338 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2339 op_text => $op_text});
2340 } elsif($1 eq 'objects') {
2341 my $sqlstr = q{
2342 select object_name "Object Name", object_type "Type", sys.login_user() "Owner"
2343 from user_objects
2344 order by object_name
2346 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2347 op_text => $op_text});
2348 } elsif($1 eq 'sequences') {
2349 my $sqlstr = q{
2350 select sequence_name "Sequence Name", 'SEQUENCE' "Type", sys.login_user() "Owner"
2351 from user_sequences
2352 order by sequence_name
2354 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2355 op_text => $op_text});
2356 } elsif($1 eq 'clusters') {
2357 my $sqlstr = q{
2358 select cluster_name "Cluster Name", 'CLUSTER' "Type", sys.login_user() "Owner"
2359 from user_clusters
2360 order by cluster_name
2362 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2363 op_text => $op_text});
2364 } elsif($1 eq 'dimensions') {
2365 my $sqlstr = q{
2366 select dimension_name "Dimension Name", 'DIMENSION' "Type", sys.login_user() "Owner"
2367 from user_dimensions
2368 order by dimension_name
2370 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2371 op_text => $op_text});
2372 } elsif($1 eq 'functions') {
2373 my $sqlstr = q{
2374 select distinct name "Function Name", 'FUNCTION' "Type", sys.login_user() "Owner"
2375 from user_source
2376 where type = 'FUNCTION'
2377 order by name
2379 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2380 op_text => $op_text});
2381 } elsif($1 eq 'procedures') {
2382 my $sqlstr = q{
2383 select distinct name "Procedure Name", 'PROCEDURE' "Type", sys.login_user() "Owner"
2384 from user_source
2385 where type = 'PROCEDURE'
2386 order by name
2388 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2389 op_text => $op_text});
2390 } elsif($1 eq 'packages') {
2391 my $sqlstr = q{
2392 select distinct name "Package Name", 'PACKAGES' "Type", sys.login_user() "Owner"
2393 from user_source
2394 where type = 'PACKAGE'
2395 order by name
2397 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2398 op_text => $op_text});
2399 } elsif($1 eq 'indexes') {
2400 my $sqlstr = q{
2401 select index_name "Index Name", 'INDEXES' "Type", sys.login_user() "Owner"
2402 from user_indexes
2403 order by index_name
2405 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2406 op_text => $op_text});
2407 } elsif($1 eq 'indextypes') {
2408 my $sqlstr = q{
2409 select indextype_name "Indextype Name", 'INDEXTYPE' "Type", sys.login_user() "Owner"
2410 from user_indextypes
2411 order by indextype_name
2413 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2414 op_text => $op_text});
2415 } elsif($1 eq 'libraries') {
2416 my $sqlstr = q{
2417 select library_name "library Name", 'LIBRARY' "Type", sys.login_user() "Owner"
2418 from user_libraries
2419 order by library_name
2421 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2422 op_text => $op_text});
2423 } elsif($1 eq 'materialized views') {
2424 my $sqlstr = q{
2425 select mview_name "Materialized View Name", 'MATERIALIZED VIEW' "Type", sys.login_user() "Owner"
2426 from user_mviews
2427 order by mview_name
2429 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2430 op_text => $op_text});
2431 } elsif($1 eq 'snapshots') {
2432 my $sqlstr = q{
2433 select name "Snapshot Name", 'SNAPSHOT' "Type", sys.login_user() "Owner"
2434 from user_snapshots
2435 order by name
2437 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2438 op_text => $op_text});
2439 } elsif($1 eq 'synonyms') {
2440 my $sqlstr = q{
2441 select synonym_name "Synonym Name", 'SYNONYM' "Type", sys.login_user() "Owner"
2442 from user_synonyms
2443 order by synonym_name
2445 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2446 op_text => $op_text});
2447 } elsif($1 eq 'triggers') {
2448 my $sqlstr = q{
2449 select trigger_name "Trigger Name", 'TRIGGER' "Type", sys.login_user() "Owner"
2450 from user_triggers
2451 order by trigger_name
2453 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2454 op_text => $op_text});
2455 } elsif($1 eq 'processes') {
2456 my $sqlstr = q{
2457 select sid,
2458 vs.username "User",
2459 vs.status "Status",
2460 vs.schemaname "Schema",
2461 vs.osuser || '@' || vs.machine "From",
2462 to_char(vs.logon_time, 'Mon DD YYYY HH:MI:SS') "Logon Time",
2463 aa.name "Command"
2464 from v$session vs, audit_actions aa
2465 where vs.command = aa.action
2466 and username is not null
2468 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2469 op_text => $op_text});
2470 } elsif($1 eq 'waits') {
2471 my $sqlstr = q{
2472 select vs.username "Username",
2473 vs.osuser "OS User",
2474 vsw.sid "SID",
2475 vsw.event "Event",
2476 decode(vsw.wait_time, -2, ' Unknown',
2477 to_char(vsw.seconds_in_wait,'999,999,999,999'))
2478 "Seconds Waiting"
2479 from v$session_wait vsw,
2480 v$session vs
2481 where vsw.sid = vs.sid
2482 and vs.status = 'ACTIVE'
2483 and vs.username is not null
2484 order by vsw.wait_time desc, vsw.seconds_in_wait desc, vsw.sid
2486 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2487 op_text => $op_text});
2488 } elsif($1 eq 'plan') {
2489 # This following query is Copyright (c) Oracle Corporation 1998, 1999. All Rights Reserved.
2490 my $sqlstr = q{
2491 select '| Operation | Name | Rows | Bytes| Cost | Pstart| Pstop |' as "Plan Table" from dual
2492 union all
2493 select '--------------------------------------------------------------------------------' from dual
2494 union all
2495 select rpad('| '||substr(lpad(' ',1*(level-1)) ||operation||
2496 decode(options, null,'',' '||options), 1, 27), 28, ' ')||'|'||
2497 rpad(substr(object_name||' ',1, 9), 10, ' ')||'|'||
2498 lpad(decode(cardinality,null,' ',
2499 decode(sign(cardinality-1000), -1, cardinality||' ',
2500 decode(sign(cardinality-1000000), -1, trunc(cardinality/1000)||'K',
2501 decode(sign(cardinality-1000000000), -1, trunc(cardinality/1000000)||'M',
2502 trunc(cardinality/1000000000)||'G')))), 7, ' ') || '|' ||
2503 lpad(decode(bytes,null,' ',
2504 decode(sign(bytes-1024), -1, bytes||' ',
2505 decode(sign(bytes-1048576), -1, trunc(bytes/1024)||'K',
2506 decode(sign(bytes-1073741824), -1, trunc(bytes/1048576)||'M',
2507 trunc(bytes/1073741824)||'G')))), 6, ' ') || '|' ||
2508 lpad(decode(cost,null,' ',
2509 decode(sign(cost-10000000), -1, cost||' ',
2510 decode(sign(cost-1000000000), -1, trunc(cost/1000000)||'M',
2511 trunc(cost/1000000000)||'G'))), 8, ' ') || '|' ||
2512 lpad(decode(partition_start, 'ROW LOCATION', 'ROWID',
2513 decode(partition_start, 'KEY', 'KEY', decode(partition_start,
2514 'KEY(INLIST)', 'KEY(I)', decode(substr(partition_start, 1, 6),
2515 'NUMBER', substr(substr(partition_start, 8, 10), 1,
2516 length(substr(partition_start, 8, 10))-1),
2517 decode(partition_start,null,' ',partition_start)))))||' ', 7, ' ')|| '|' ||
2518 lpad(decode(partition_stop, 'ROW LOCATION', 'ROW L',
2519 decode(partition_stop, 'KEY', 'KEY', decode(partition_stop,
2520 'KEY(INLIST)', 'KEY(I)', decode(substr(partition_stop, 1, 6),
2521 'NUMBER', substr(substr(partition_stop, 8, 10), 1,
2522 length(substr(partition_stop, 8, 10))-1),
2523 decode(partition_stop,null,' ',partition_stop)))))||' ', 7, ' ')||'|' as "Explain plan"
2524 from plan_table
2525 start with id=0 and timestamp = (select max(timestamp) from plan_table where id=0)
2526 connect by prior id = parent_id
2527 and prior nvl(statement_id, ' ') = nvl(statement_id, ' ')
2528 and prior timestamp <= timestamp
2529 union all
2530 select '--------------------------------------------------------------------------------' from dual
2532 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2533 op_text => $op_text});
2534 } elsif($1 eq 'errors') {
2535 my $err = $dbh->func( 'plsql_errstr' );
2536 if($err) {
2537 print "\n$err\n\n";
2538 } else {
2539 print "\nNo errors.\n\n";
2541 } elsif($1 eq 'users') {
2542 my $sqlstr = q{
2543 select username, user_id, created
2544 from all_users
2546 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2547 op_text => $op_text});
2548 } elsif($1 eq 'user') {
2549 my $sqlstr = q{
2550 select user from dual
2552 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2553 op_text => $op_text});
2554 } elsif($1 eq 'uid') {
2555 my $sqlstr = q{
2556 select uid from dual
2558 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2559 op_text => $op_text});
2560 } elsif(($1 eq 'database links') || ($1 eq 'dblinks')) {
2561 my $sqlstr = q{
2562 select db_link, host, owner from all_db_links
2564 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2565 op_text => $op_text});
2566 } else {
2567 query_err("show", "Unsupported show type", $input);
2569 } else {
2570 query_err("show", "Unsupported show type", $input);
2576 sub describe {
2577 my($input, $format, $nosynonym, $num_rows, $op, $op_text) = @_;
2578 debugmsg(3, "describe called", @_);
2579 # This describes a table, view, sequence, or synonym by listing it's
2580 # columns and their attributes
2582 # convert to lowercase for comparison operations
2583 $input = lc($input);
2585 # make sure we're still connected to the database
2586 unless(ping()) {
2587 wrn("Database connection died");
2588 db_reconnect();
2591 # parse the query to find the table that was requested to be described
2592 if($input =~ /^\s*desc\w*\s*([a-zA-Z0-9_\$\#\.\@]+)/) {
2593 my $object = $1;
2594 my $sqlstr;
2595 my $type;
2596 my @ret;
2598 my $schema;
2599 my $dblink;
2600 if($object =~ /^([a-zA-Z0-9_\$\#]+)\.([a-zA-Z0-9_\$\#]+)\@([a-zA-Z0-9_\$\#]+)$/) {
2601 $schema = $1;
2602 $object = $2;
2603 $dblink = "\@$3";
2604 } elsif($object =~ /^([a-zA-Z0-9_\$\#]+)\@([a-zA-Z0-9_\$\#]+)$/) {
2605 $schema = $dbuser;
2606 $object = $1;
2607 $dblink = "\@$2";
2608 } elsif($object =~ /^([a-zA-Z0-9_\$\#]+)\.([a-zA-Z0-9_\$\#]+)$/) {
2609 $schema = $1;
2610 $object = $2;
2611 } else {
2612 $schema = $dbuser;
2615 debugmsg(1,"schema: [$schema] object: [$object] dblink: [$dblink]");
2617 if($conf{fast_describe}) {
2618 if(my $sth = $dbh->prepare("select * from $schema.$object$dblink")) {
2619 my $fields = $sth->{NAME};
2620 my $types = $sth->{TYPE};
2621 my $type_info = $dbh->type_info($types->[0]);
2622 my $precision = $sth->{PRECISION};
2623 my $scale = $sth->{SCALE};
2624 my $nullable = $sth->{NULLABLE};
2626 debugmsg(4, "fields: [" . join(',', @$fields) . "]");
2627 debugmsg(4, "types: [" . join(',', @$types) . "]");
2628 debugmsg(4, "type_info: [" . Dumper($type_info) . "]");
2629 debugmsg(4, "precision: [" . join(',', @$precision) . "]");
2630 debugmsg(4, "scale: [" . join(',', @$scale) . "]");
2631 debugmsg(4, "nullable: [" . join(',', @$nullable) . "]");
2633 # Assemble a multidiminsional array of the output
2634 my @desc;
2635 for(my $i = 0; $i < @$fields; $i++) {
2636 my ($name, $null, $type);
2637 $name = $fields->[$i];
2638 $null = ($nullable->[$i] ? 'NULL' : 'NOT NULL');
2639 my $type_info = $dbh->type_info($types->[$i]);
2640 $type = $type_info->{'TYPE_NAME'};
2641 # convert DECIMAL to NUMBER for our purposes (some kind of DBD kludge)
2642 $type = 'NUMBER' if $type eq 'DECIMAL';
2643 if( $type eq 'VARCHAR2' || $type eq 'NVARCHAR2' ||
2644 $type eq 'CHAR' || $type eq 'NCHAR' || $type eq 'RAW' )
2646 $type .= "($precision->[$i])";
2647 } elsif($type eq 'NUMBER' && ($scale->[$i] || $precision->[$i] < 38))
2649 $type .= "($precision->[$i],$scale->[$i])";
2651 push(@desc, [$name, $null, $type]);
2654 # figure max column sizes we'll need
2655 my @widths = (4,5,4);
2656 for(my $i = 0; $i < @desc; $i++) {
2657 for(my $j = 0; $j < @{$desc[0]}; $j++) {
2658 if(length($desc[$i][$j]) > $widths[$j]) {
2659 $widths[$j] = length($desc[$i][$j]);
2664 # open the redirection file
2665 if($op && $op eq '>' || $op eq '>>') {
2666 ($op_text) = glob($op_text);
2667 debugmsg(3, "Opening file '$op_text' for output redirection using [$op]");
2668 open(FOUT, $op . $op_text) || do query_err('redirect',"Cannot open file '$op_text' for writing: $!", '');
2669 } elsif($op eq '|') {
2670 debugmsg(3, "Opening pipe to '$op_text' for output redirection");
2671 open(FOUT, $op . $op_text) || do query_err('pipe',"Cannot open pipe '$op_text': $!", '');
2672 } else {
2673 open(FOUT, ">&STDOUT");
2676 if($opt_headers) {
2677 # Print headers
2678 print FOUT "\n";
2679 print FOUT sprintf("%-$widths[0]s", 'Name')
2680 . ' '
2681 . sprintf("%-$widths[1]s", 'Null?')
2682 . ' '
2683 . sprintf("%-$widths[2]s", 'Type')
2684 . "\n";
2685 print FOUT '-' x $widths[0]
2686 . ' '
2687 . '-' x $widths[1]
2688 . ' '
2689 . '-' x $widths[2]
2690 . "\n";
2692 for(my $i = 0; $i < @desc; $i++) {
2693 for(my $j = 0; $j < @{$desc[$i]}; $j++) {
2694 print FOUT ' ' if $j > 0;
2695 print FOUT sprintf("%-$widths[$j]s", $desc[$i][$j]);
2697 print FOUT "\n";
2699 print FOUT "\n";
2701 close(FOUT);
2703 return();
2707 # look in all_constraints for the object first. This is because oracle
2708 # stores information about primary keys in the all_objects table as "index"s
2709 # but it doesn't have foreign keys or constraints. So we want to match
2710 # there here first
2712 # now look in all_objects
2713 my $all_object_cols = 'object_type,owner,object_name,'
2714 . 'object_id,created,last_ddl_time,'
2715 . 'timestamp,status';
2717 @ret = $dbh->selectrow_array(
2718 "select $all_object_cols from all_objects where object_name = ? "
2719 ."and owner = ?"
2720 .($nosynonym ? " and object_type != 'SYNONYM'" : ""),
2721 undef, uc($object), uc($schema)
2722 ) or
2723 @ret = $dbh->selectrow_array(
2724 "select $all_object_cols from all_objects where object_name = ? "
2725 ."and owner = 'PUBLIC'"
2726 .($nosynonym ? " and object_type != 'SYNONYM'" : ""),
2727 undef, uc($object)
2730 unless(@ret) {
2731 @ret = $dbh->selectrow_array(
2732 "select constraint_type, constraint_name from all_constraints where "
2733 ."constraint_name = ?",
2734 undef, uc($object)
2738 if($ret[0] eq 'INDEX') {
2739 # Check if this 'index' is really a primary key and is in the
2740 # all_constraints table
2742 my @temp_ret = $dbh->selectrow_array(
2743 "select constraint_type, constraint_name from all_constraints where "
2744 ."constraint_name = ?",
2745 undef, uc($object)
2748 @ret = @temp_ret if @temp_ret;
2751 $type = $ret[0];
2752 debugmsg(1,"type: [$type] ret: [@ret]");
2754 if($type eq 'SYNONYM') {
2755 # Find what this is a synonym to, then recursively call this function
2756 # again to describe whatever it points to
2757 my($table_name, $table_owner) = $dbh->selectrow_array(
2758 'select table_name, table_owner from all_synonyms '
2759 .'where synonym_name = ? and owner = ?',
2760 undef, uc($ret[2]), uc($ret[1])
2763 describe("desc $table_owner.$table_name", $format, 1);
2764 } elsif($type eq 'SEQUENCE') {
2765 my $sqlstr = q{
2766 select sequence_name "Name",
2767 min_value "Min",
2768 max_value "Max",
2769 increment_by "Inc",
2770 cycle_flag "Cycle",
2771 order_flag "Order",
2772 last_number "Last"
2773 from all_sequences
2774 where sequence_name = ?
2775 and sequence_owner = ?
2777 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2778 op_text => $op_text}, uc($ret[2]), uc($ret[1]));
2779 } elsif($type eq 'TABLE' || $type eq 'VIEW' || $type eq 'TABLE PARTITION') {
2780 my $sqlstr = q{
2781 select column_name "Name",
2782 decode(nullable,
2783 'N','NOT NULL'
2784 ) "Null?",
2785 decode(data_type,
2786 'VARCHAR2','VARCHAR2(' || TO_CHAR(data_length) || ')',
2787 'NVARCHAR2','NVARCHAR2(' || TO_CHAR(data_length) || ')',
2788 'CHAR','CHAR(' || TO_CHAR(data_length) || ')',
2789 'NCHAR','NCHAR(' || TO_CHAR(data_length) || ')',
2790 'NUMBER',
2791 decode(data_precision,
2792 NULL, 'NUMBER',
2793 'NUMBER(' || TO_CHAR(data_precision)
2794 || ',' || TO_CHAR(data_scale) || ')'
2796 'FLOAT',
2797 decode(data_precision,
2798 NULL, 'FLOAT', 'FLOAT(' || TO_CHAR(data_precision) || ')'
2800 'DATE','DATE',
2801 'LONG','LONG',
2802 'LONG RAW','LONG RAW',
2803 'RAW','RAW(' || TO_CHAR(data_length) || ')',
2804 'MLSLABEL','MLSLABEL',
2805 'ROWID','ROWID',
2806 'CLOB','CLOB',
2807 'NCLOB','NCLOB',
2808 'BLOB','BLOB',
2809 'BFILE','BFILE',
2810 data_type || ' ???'
2811 ) "Type",
2812 data_default "Default"
2813 from all_tab_columns atc
2814 where table_name = ?
2815 and owner = ?
2816 order by column_id
2818 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2819 op_text => $op_text}, uc($ret[2]), uc($ret[1]));
2820 } elsif($type eq 'R') {
2821 my $sqlstr = q{
2822 select ac.constraint_name "Name",
2823 decode(ac.constraint_type,
2824 'R', 'Foreign Key',
2825 'C', 'Check',
2826 'U', 'Unique',
2827 'P', 'Primary Key',
2828 ac.constraint_type) "Type",
2829 ac.table_name "Table Name",
2830 acc.column_name "Column Name",
2831 r_ac.table_name "Parent Table",
2832 r_acc.column_name "Parent Column",
2833 ac.delete_rule "Delete Rule"
2834 from all_constraints ac, all_cons_columns acc,
2835 all_constraints r_ac, all_cons_columns r_acc
2836 where ac.constraint_name = acc.constraint_name
2837 and ac.owner = acc.owner
2838 and ac.r_constraint_name = r_ac.constraint_name
2839 and r_ac.constraint_name = r_acc.constraint_name
2840 and r_ac.owner = r_acc.owner
2841 and ac.constraint_type = 'R'
2842 and ac.constraint_name = ?
2843 and ac.owner = ?
2844 order by ac.constraint_name, acc.position
2846 query($sqlstr, 'list_aligned', {num_rows => $num_rows, op => $op,
2847 op_text => $op_text}, uc($ret[1]),
2848 uc($schema));
2849 } elsif($type eq 'P' || $type eq 'U') {
2850 my $sqlstr = q{
2851 select ac.constraint_name "Name",
2852 decode(ac.constraint_type,
2853 'R', 'Foreign Key',
2854 'C', 'Check',
2855 'U', 'Unique',
2856 'P', 'Primary Key',
2857 ac.constraint_type) "Type",
2858 ac.table_name "Table Name",
2859 acc.column_name "Column Name"
2860 from all_constraints ac, all_cons_columns acc
2861 where ac.constraint_name = acc.constraint_name
2862 and ac.owner = acc.owner
2863 and ac.constraint_name = ?
2864 and ac.owner = ?
2865 order by ac.constraint_name, acc.position
2867 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2868 op_text => $op_text}, uc($ret[1]), uc($schema));
2869 } elsif($type eq 'C') {
2870 my $sqlstr = q{
2871 select ac.constraint_name "Name",
2872 decode(ac.constraint_type,
2873 'R', 'Foreign Key',
2874 'C', 'Check',
2875 'U', 'Unique',
2876 'P', 'Primary Key',
2877 ac.constraint_type) "Type",
2878 ac.table_name "Table Name",
2879 ac.search_condition "Search Condition"
2880 from all_constraints ac
2881 where ac.constraint_name = ?
2882 order by ac.constraint_name
2884 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2885 op_text => $op_text}, uc($ret[1]));
2886 } elsif($type eq 'INDEX') {
2887 my $sqlstr = q{
2888 select ai.index_name "Index Name",
2889 ai.index_type "Type",
2890 ai.table_name "Table Name",
2891 ai.uniqueness "Unique?",
2892 aic.column_name "Column Name"
2893 from all_indexes ai, all_ind_columns aic
2894 where ai.index_name = aic.index_name(+)
2895 and ai.table_owner = aic.table_owner(+)
2896 and ai.index_name = ?
2897 and ai.table_owner = ?
2898 order by aic.column_position
2900 query($sqlstr, $format, {num_rows => $num_rows, op => $op,
2901 op_text => $op_text}, uc($ret[2]), uc($schema));
2902 } elsif($type eq 'TRIGGER') {
2903 my $sqlstr = q{
2904 select trigger_name "Trigger Name",
2905 trigger_type "Type",
2906 triggering_event "Event",
2907 table_name "Table",
2908 when_clause "When",
2909 description "Description",
2910 trigger_body "Body"
2911 from all_triggers
2912 where trigger_name = ?
2914 query($sqlstr, 'list_aligned', {num_rows => $num_rows, op => $op,
2915 op_text => $op_text}, uc($ret[2]));
2916 } elsif($type eq 'PACKAGE') {
2917 wrn("Not implemented (yet)");
2918 } elsif($type eq 'PROCEDURE') {
2919 wrn("Not implemented (yet)");
2920 } elsif($type eq 'CLUSTER') {
2921 wrn("Not implemented (yet)");
2922 } elsif($type eq 'TRIGGER') {
2923 wrn("Not implemented (yet)");
2924 } else {
2925 query_err('describe', "Object $object not found");
2929 sub let_cmd {
2930 my($input) = @_;
2931 debugmsg(3, "let_cmd called", @_);
2932 my @book_keys = qw/sql_query_in_error auto_complete edit_history fast_describe complete_objects complete_tables extended_complete_list extended_benchmarks column_wildcards complete_columns auto_commit commit_on_exit command_complete_list long_trunc_ok/;
2934 if ($input =~ /^\s*let\s*(\w+)?\s*/i ){
2935 my @print_keys = keys %conf;
2936 @print_keys = grep(/$1/,@print_keys) if ($1);
2938 foreach my $key ( @print_keys ){
2939 my $print_conf = $conf{$key};
2940 $print_conf = ($conf{$key}) ? 'On' : 'Off' if ( grep(/$key/,@book_keys) ) ;
2941 printf("%25s : %1s\n",$key,$print_conf);
2943 }else{
2944 print "usage let <config name>\n";
2947 sub set_cmd {
2948 my($input) = @_;
2949 debugmsg(3, "set_cmd called", @_);
2950 # This mimics SQL*Plus set commands, or ignores them completely. For those
2951 # that are not supported, we do nothing at all, but return silently.
2953 if($input =~ /^\s*set\s+serverout(?:put)?\s+(on|off)(?:\s+size\s+(\d+))?/i) {
2954 if(lc($1) eq 'on') {
2955 my $size = $2 || 1_000_000;
2956 debugmsg(2, "calling dbms_output_enable($size)");
2957 $dbh->func( $size, 'dbms_output_enable' )
2958 or warn "dbms_output_enable($size) failed: $DBI::errstr\n";
2959 $set{serveroutput} = 1;
2960 debugmsg(2, "serveroutput set to $set{serveroutput}");
2961 } else {
2962 $set{serveroutput} = 0;
2963 debugmsg(2, "serveroutput set to $set{serveroutput}");
2965 }elsif($input =~ /^\s*set\s+(long_read_len|LongReadLen)\s+(\d+)/i){
2966 debugmsg(2, "long_read_len/LongReadLen set to $2");
2967 $dbh->{LongReadLen} = $2;
2968 }elsif($input =~ /^\s*set\s+fast_describe\s+(on|off)/i){
2969 $conf{fast_describe} = (lc($1) eq 'on') ? 1 : 0;
2970 print "fast_describe is now " . ($conf{fast_describe} ? 'on' : 'off') . "\n";
2972 }elsif($input =~ /^\s*set\s+(\w+)\s*/ ){
2973 print "Can't set option $1\n";
2977 sub query {
2978 my($sqlstr, $format, $opts, @bind_vars) = @_;
2979 debugmsg(3, "query called", @_);
2980 # this runs the provided query and calls format_display to display the results
2982 my $num_rows = $opts->{num_rows};
2983 my $op = $opts->{op};
2984 my $op_text = $opts->{op_text};
2985 my $result_output = ( exists $opts->{result_output}
2986 ? $opts->{result_output}
2990 my(@totalbench, @querybench, @formatbench);
2992 # Look for special query types, such as "show" and "desc" that we handle
2993 # and don't send to the database at all, since they're not really valid SQL.
2995 my ($rows_affected, $success_code);
2997 if($sqlstr =~ /^\s*desc/i) {
2998 describe($sqlstr, $format, undef, $num_rows, $op, $op_text);
2999 } elsif($sqlstr =~ /^\s*show/i) {
3000 show($sqlstr, $format, $num_rows, $op, $op_text);
3001 } else {
3002 $running_query = 1;
3004 # make sure we're still connected to the database
3005 unless(ping()) {
3006 wrn("Database connection died");
3007 db_reconnect();
3010 $sqlstr = wildcard_expand($sqlstr) if $conf{column_wildcards};
3012 # send the query on to the database
3013 push(@totalbench, get_bench()) if !$conf{extended_benchmarks};
3014 push(@querybench, get_bench()) if $conf{extended_benchmarks};
3015 debugmsg(3, "preparing", $sqlstr);
3016 my $sth = $dbh->prepare($sqlstr);
3017 unless($sth) {
3018 my $err = $DBI::errstr;
3019 $err =~ s/ \(DBD ERROR\: OCIStmtExecute\/Describe\)//;
3021 if ($err =~ m/DBD ERROR\:/) {
3022 my $indicator_offset = $DBI::errstr;
3023 $indicator_offset =~ s/(.*)(at\ char\ )(\d+)(\ .*)/$3/;
3024 if ($indicator_offset > 0) {
3025 my $i = 0;
3026 print $sqlstr, "\n";
3027 for ($i=0;$i<$indicator_offset;++$i) {
3028 print " ";
3030 print "*\n";
3034 # Output message if serveroutput is on
3035 if($set{serveroutput}) {
3036 debugmsg(3, "Calling dmbs_output_get");
3037 my @output = $dbh->func( 'dbms_output_get' );
3038 print join("\n", @output) . "\n";
3040 query_err('prepare', $err, $sqlstr), setup_sigs(), return();
3042 debugmsg(2, "sth: [$sth]");
3044 $cursth = $sth;
3046 finish_query($sth), return() if $sigintcaught; #pseudo sig handle
3048 my $ret;
3049 eval {
3050 debugmsg(3, "executing", $sqlstr);
3051 $ret = $sth->execute(@bind_vars);
3053 debugmsg(3, "ret:", $ret, "\@:", $@, "\$DBI::errstr:", $DBI::errstr);
3054 if(!$ret) {
3055 my $eval_error = $@;
3056 $eval_error =~ s/at \(eval \d+\) line \d+, <\S+> line \d+\.//;
3057 my $err = $DBI::errstr;
3058 $err =~ s/ \(DBD ERROR: OCIStmtExecute\)//;
3059 # Output message is serveroutput is on
3060 if($set{serveroutput}) {
3061 debugmsg(3, "Calling dmbs_output_get");
3062 my @output = $dbh->func( 'dbms_output_get' );
3063 print join("\n", @output) . "\n";
3065 my $errstr = ($eval_error ? $eval_error : $err);
3066 query_err('execute', $errstr, $sqlstr);
3067 setup_sigs();
3068 return();
3071 if($DBI::errstr =~ /^ORA-24344/) {
3072 print "\nWarning: Procedure created with compilation errors.\n\n";
3073 setup_sigs();
3074 return();
3077 push(@querybench, get_bench()) if $conf{extended_benchmarks};
3079 finish_query($sth), return() if $sigintcaught; #pseudo sig handle
3081 debugmsg(1, "rows returned: [" . $sth->rows() . "]");
3083 # open the redirection file
3084 if($op && $op eq '>' || $op eq '>>') {
3085 ($op_text) = glob($op_text);
3086 debugmsg(3, "Opening file '$op_text' for output redirection using [$op]");
3087 open(FOUT, $op . $op_text) || do{
3088 query_err('redirect',"Cannot open file '$op_text' for writing: $!",
3089 $sqlstr);
3090 finish_query($sth);
3091 return();
3093 } elsif($op eq '|') {
3094 debugmsg(3, "Opening pipe to '$op_text' for output redirection");
3095 open(FOUT, $op . $op_text) || do{
3096 query_err('pipe',"Cannot open pipe '$op_text': $!", $sqlstr);
3097 finish_query($sth);
3098 return();
3100 } else {
3101 open(FOUT, ">&STDOUT");
3104 # Output message is serveroutput is on
3105 if($set{serveroutput}) {
3106 debugmsg(3, "Calling dmbs_output_get");
3107 my @output = $dbh->func( 'dbms_output_get' );
3108 print join("\n", @output) . "\n";
3111 # Determine type and output accordingly
3112 if($sqlstr =~ /^\s*declare|begin/i) {
3113 print STDERR "\nPL/SQL procedure successfully completed.\n\n";
3114 } else {
3115 push(@formatbench, get_bench()) if $conf{extended_benchmarks};
3116 ($rows_affected, $success_code) = format_output($sth, $format, $num_rows,
3117 $sqlstr, $op, $op_text)
3118 or finish_query($sth), return();
3119 push(@formatbench, get_bench()) if $conf{extended_benchmarks};
3120 push(@totalbench, get_bench()) if !$conf{extended_benchmarks};
3122 finish_query($sth), return() if $sigintcaught; #pseudo sig handle
3124 # output format_affected
3125 if($result_output) {
3126 if(!$opt_batch) {
3127 print STDERR "\n" . format_affected($rows_affected, $success_code);
3130 if(!$opt_batch) {
3131 if($opt_bench || $conf{extended_benchmarks}) {
3132 print STDERR "\n\n";
3133 print STDERR ('-' x 80);
3134 print STDERR "\n";
3135 output_benchmark("Query: ", @querybench, "\n");
3136 output_benchmark("Format:", @formatbench, "\n");
3137 } else {
3138 output_benchmark(" (", @totalbench, ")");
3139 print STDERR "\n";
3141 print STDERR "\n";
3146 close(FOUT);
3148 finish_query($sth);
3150 undef($sth);
3151 undef($cursth);
3154 return($rows_affected, $success_code);
3157 sub wildcard_expand {
3158 my($sql) = @_;
3159 debugmsg(3, "wildcard_expand called", @_);
3161 my $newsql = $sql;
3162 my $fromstuff;
3163 my $wheregrouporder = $sql;
3164 $wheregrouporder =~ s/.*(where|order|group).*/\1/;
3165 if ($wheregrouporder eq $sql) {
3166 $wheregrouporder = "";
3168 ($sql,$fromstuff) = split(/order|group|where/i,$sql,2);
3169 if ($sql =~ /^select\s+(.+?)\*\s+from\s+(.+)/i) {
3170 debugmsg(1, "Match made: ($1) ($2)");
3171 my $wildcardstring = uc($1);
3172 my $tablename = uc($2);
3173 my @tlist = split(/,/,$tablename);
3174 my $tablelist = "";
3175 my %column_prefix;
3176 foreach my $table (@tlist) {
3177 $table =~ s/^ *//;
3178 $table =~ s/([^ ]+)\s+(.*)/\1/;
3179 $column_prefix{$table} = $2 ? $2 : $table;
3180 $tablelist .= ($tablelist ? "," : "") . $table;
3182 $tablelist =~ s/,/' or table_name='/g;
3183 my $qstr = "select table_name||'.'||column_name from all_tab_columns where (table_name='$tablelist') and column_name like '$wildcardstring%' escape '\\'";
3184 debugmsg(1, "qstr: [$qstr]");
3185 my $sth = $dbh->prepare($qstr);
3186 $sth->execute();
3187 setup_sigs();
3188 my $colname;
3189 my $collist;
3190 while ( ($colname) = $sth->fetchrow_array() ) {
3191 foreach my $table (keys %column_prefix) {
3192 $colname =~ s/$table\./$column_prefix{$table}\./;
3193 $colname =~ s/ //g;
3195 $collist .= ($collist ? "," : "") . $colname;
3197 $collist = $collist ? $collist : "*";
3198 $newsql = "select " . $collist . " from " . $tablename . " "
3199 . $wheregrouporder . " " . $fromstuff;
3200 debugmsg(1, "newsql: [$newsql]");
3202 $newsql;
3205 sub finish_query {
3206 my($sth) = @_;
3207 # This just finishes the query and cleans up the state info
3209 $sth->finish;
3210 undef($cursth);
3211 $running_query = 0;
3212 setup_sigs();
3215 sub get_bench {
3216 debugmsg(3, "get_bench called", @_);
3217 # returns benchmark info
3219 my($benchmark, $hires);
3220 $benchmark = new Benchmark;
3222 if($nohires) {
3223 $hires = time;
3224 } else {
3225 # use an eval to keep perl from syntax checking it unless we have the
3226 # Time::HiRes module loaded
3227 eval q{
3228 $hires = [gettimeofday]
3232 return($benchmark, $hires);
3235 sub output_benchmark {
3236 my($string, $bstart, $hrstart, $bend, $hrend, $string2) = @_;
3237 debugmsg(3, "output_benchmark called", @_);
3238 # This just outputs the benchmark info
3240 my $bench = timediff($bend, $bstart);
3242 my $time;
3243 if($nohires) {
3244 # the times will be seconds
3245 $time = $hrend - $hrstart;
3246 } else {
3247 eval q{$time = tv_interval($hrstart, $hrend)};
3248 $time = sprintf("%.2f", $time);
3251 unless($opt_bench || $conf{extended_benchmarks}) {
3252 # convert $time to something more readable
3253 $time =~ s/\.(\d+)$//;
3254 my $decimal = $1;
3255 my @tparts;
3256 my $tmp;
3257 if(($tmp = int($time / 604800)) >= 1) {
3258 push(@tparts, "$tmp week" . ($tmp != 1 && 's'));
3259 $time %= 604800;
3261 if(($tmp = int($time / 86400)) >= 1) {
3262 push(@tparts, "$tmp day" . ($tmp != 1 && 's'));
3263 $time %= 86400;
3265 if(($tmp = int($time / 3600)) >= 1) {
3266 push(@tparts, "$tmp hour" . ($tmp != 1 && 's'));
3267 $time %= 3600;
3269 if(($tmp = int($time / 60)) >= 1) {
3270 push(@tparts, "$tmp minute" . ($tmp != 1 && 's'));
3271 $time %= 60;
3273 $time ||= '0';
3274 $decimal ||= '00';
3275 $time .= ".$decimal";
3276 push(@tparts, "$time second" . ($time != 1 && 's'));
3277 $time = join(", ", @tparts);
3280 if($opt_bench || $conf{extended_benchmarks}) {
3281 print STDERR "$string\[ $time second" . ($time != 1 && 's')
3282 . " ] [" . timestr($bench) . " ]$string2";
3283 } else {
3284 print STDERR "$string$time$string2";
3288 sub format_output {
3289 my($sth, $format, $num_rows, $sqlstr, $op, $op_text) = @_;
3290 debugmsg(3, "format_output called", @_);
3291 # Formats the output according to the query terminator. If it was a ';' or
3292 # a '/' then a normal table is output. If it was a '\g' then all the columns # and rows are output put line by line.
3293 # input: $sth $format
3294 # sth is the statement handler
3295 # format can be either 'table', 'list', or 'list_aligned'
3296 # output: returns 0 on error, ($success_code, $rows_affected) on success
3297 # $success_code = ('select', 'affected');
3299 debugmsg(3,"type: [" . Dumper($sth->{TYPE}) . "]");
3301 # Is this query a select?
3302 my $isselect = 1 if $sqlstr =~ /^\s*select/i;
3304 if($format eq 'table') {
3305 my $count = 0;
3306 my $res = [];
3307 my $overflow = 0;
3308 while(my @res = $sth->fetchrow_array()) {
3309 push(@$res, \@res);
3310 $count++;
3311 if($count > 1000) {
3312 debugmsg(1,"overflow in table output, switching to serial mode");
3313 $overflow = 1;
3314 last;
3316 debugmsg(1,"num_rows hit on fetch") if $num_rows && $count >= $num_rows;
3317 last if $num_rows && $count >= $num_rows;
3318 return(0) if $sigintcaught; #pseudo sig handle
3321 # If we didn't get any rows back, then the query was probably an insert or
3322 # update, so we call format_affected
3323 if(@$res <= 0 && !$isselect) {
3324 return($sth->rows(), 'affected');
3327 return(0) if $sigintcaught; #pseudo sig handle
3329 # First go through all the return data to determine column widths
3330 my @widths;
3331 for( my $i = 0; $i < @{$res}; $i++ ) {
3332 for( my $j = 0; $j < @{$res->[$i]}; $j++ ) {
3333 if(length($res->[$i]->[$j]) > $widths[$j]) {
3334 $widths[$j] = length($res->[$i]->[$j]);
3337 return(0) if $sigintcaught; #pseudo sig handle
3338 debugmsg(1,"num_rows hit on calc") if $num_rows && $i >= $num_rows-1;
3339 last if $num_rows && $i >= $num_rows-1;
3342 return(0) if $sigintcaught; #pseudo sig handle
3344 my $fields = $sth->{NAME};
3345 my $types = $sth->{TYPE};
3346 my $nullable = $sth->{NULLABLE};
3348 debugmsg(4, "fields: [" . Dumper($fields) . "]");
3349 debugmsg(4, "types: [" . Dumper($types) . "]");
3350 debugmsg(4, "nullable: [" . Dumper($nullable) . "]");
3352 return(0) if $sigintcaught; #pseudo sig handle
3354 # Extend the column widths if the column name is longer than any of the
3355 # data, so that it doesn't truncate the column name
3356 for( my $i = 0; $i < @$fields; $i++ ) {
3357 if(length($fields->[$i]) > $widths[$i]) {
3358 debugmsg(3, "Extending $fields->[$i] for name width");
3359 $widths[$i] = length($fields->[$i]);
3361 return(0) if $sigintcaught; #pseudo sig handle
3364 return(0) if $sigintcaught; #pseudo sig handle
3366 # Extend the column widths if the column is NULLABLE so that we'll
3367 # have room for 'NULL'
3368 for( my $i = 0; $i < @$nullable; $i++ ) {
3369 if($nullable->[$i] && $widths[$i] < 4) {
3370 debugmsg(3, "Extending $fields->[$i] for null");
3371 $widths[$i] = 4;
3373 return(0) if $sigintcaught; #pseudo sig handle
3376 return(0) if $sigintcaught; #pseudo sig handle
3378 my $sumwidths;
3379 foreach(@widths) {
3380 $sumwidths += $_;
3383 return(0) if $sigintcaught; #pseudo sig handle
3385 debugmsg(2,"fields: [" . join("|", @$fields) . "] sumwidths: [$sumwidths] widths: [" . join("|", @widths) . "]\n");
3387 return(0) if $sigintcaught; #pseudo sig handle
3389 # now do the actual outputting, starting with the header
3390 my $rows_selected = 0;
3391 if(@$res) {
3392 if(!$opt_batch) {
3393 print FOUT "\r\e[K" if $op eq '<';
3394 print FOUT "\n";
3395 for( my $i = 0; $i < @$fields; $i++ ) {
3396 if($opt_batch) {
3397 print FOUT "\t" if $i > 0;
3398 print FOUT sprintf("%s", $fields->[$i]);
3400 else
3402 print FOUT " " if $i > 0;
3403 if($types->[$i] == 3 || $types->[$i] == 8) {
3404 print FOUT sprintf("%$widths[$i]s", $fields->[$i]);
3405 } else {
3406 print FOUT sprintf("%-$widths[$i]s", $fields->[$i]);
3410 print FOUT "\n";
3412 for( my $i = 0; $i < @$fields; $i++ ) {
3413 print FOUT " " if $i > 0;
3414 print FOUT '-' x $widths[$i];
3416 print FOUT "\n";
3419 return(0) if $sigintcaught; #pseudo sig handle
3421 # now print the actual data rows
3422 my $count = 0;
3423 for( my $j = 0; $j < @$res; $j++ ) {
3424 $count = $j;
3425 for( my $i = 0; $i < @$fields; $i++ ) {
3426 print FOUT " " if $i > 0;
3427 my $data = $res->[$j]->[$i];
3428 # Strip out plain ole \r's since SQL*Plus seems to...
3429 $data =~ s/\r//g;
3430 $data = 'NULL' unless defined $data;
3431 if($types->[$i] == 3 || $types->[$i] == 8) {
3432 print FOUT sprintf("%$widths[$i]s", $data);
3433 } else {
3434 print FOUT sprintf("%-$widths[$i]s", $data);
3437 print FOUT "\n";
3439 $rows_selected++;
3440 debugmsg(2,"num_rows hit on output") if $num_rows && $j >= $num_rows-1;
3441 last if $num_rows && $j >= $num_rows-1;
3442 return(0) if $sigintcaught; #pseudo sig handle
3445 if($overflow) {
3446 # output the rest of the data from the statement handler
3447 while(my $res = $sth->fetch()) {
3448 $count++;
3449 for( my $i = 0; $i < @$fields; $i++ ) {
3450 print FOUT " " if $i > 0;
3451 my $data = substr($res->[$i],0,$widths[$i]);
3452 # Strip out plain ole \r's since SQL*Plus seems to...
3453 $data =~ s/\r//g;
3454 $data = 'NULL' unless defined $data;
3455 if($types->[$i] == 3 || $types->[$i] == 8) {
3456 print FOUT sprintf("%$widths[$i]s", $data);
3457 } else {
3458 print FOUT sprintf("%-$widths[$i]s", $data);
3461 print FOUT "\n";
3463 $rows_selected++;
3464 debugmsg(2,"num_rows hit on output")
3465 if $num_rows && $count >= $num_rows-1;
3466 last if $num_rows && $count >= $num_rows-1;
3467 return(0) if $sigintcaught; #pseudo sig handle
3472 return($rows_selected, 'selected');
3474 } elsif($format eq 'list' || $format eq 'quiet-list' ) {
3475 # output in a nice list format, which is where we print each row in turn,
3476 # with each column on it's own line
3477 # quiet-list doesn't displat *** Row...
3478 my $quiet = ($format eq 'quiet-list') ? 1 : 0;
3479 my $fields = $sth->{NAME};
3481 print "\r\e[K" if $op eq '<';
3482 print FOUT "\n";
3484 my $count = 0;
3485 while(my $res = $sth->fetch()) {
3486 print FOUT "\n**** Row: " . ($count+1) . "\n" unless ($quiet);
3487 for( my $i = 0; $i < @$fields; $i++ ) {
3488 my $data = $res->[$i];
3489 $data = 'NULL' unless defined $data;
3490 if ($quiet) {
3491 print FOUT $data . "\n";
3492 }else{
3493 print FOUT $fields->[$i] . ": " . $data . "\n";
3496 $count++;
3497 last if $num_rows && $count >= $num_rows;
3498 return(0) if $sigintcaught; #pseudo sig handle
3501 return(0) if $sigintcaught; #pseudo sig handle
3503 # If we didn't get any rows back, then the query was probably an insert or
3504 # update, so we call format_affected
3505 if($count <= 0 && !$isselect) {
3506 return($sth->rows(), 'affected');
3509 return($count, 'selected');
3511 } elsif($format eq 'list_aligned') {
3512 # output in a nice list format, which is where we print each row in turn,
3513 # with each column on it's own line. The column names are aligned in this
3514 # one (so that the data all starts on the same column)
3516 my $fields = $sth->{NAME};
3518 print "\r\e[K" if $op eq '<';
3519 print FOUT "\n";
3521 my $maxwidth = 0;
3522 for( my $i = 0; $i < @$fields; $i++ ) {
3523 my $len = length($fields->[$i]) + 1; # +1 for the colon
3524 $maxwidth = $len if $len >= $maxwidth;
3527 return(0) if $sigintcaught; #pseudo sig handle
3529 my $count = 0;
3530 while(my $res = $sth->fetch()) {
3531 print FOUT "\n**** Row: " . ($count+1) . "\n";
3532 for( my $i = 0; $i < @$fields; $i++ ) {
3533 my $data = $res->[$i];
3534 $data = 'NULL' unless defined $data;
3535 print FOUT sprintf("%-" . $maxwidth . "s", $fields->[$i] . ":");
3536 print FOUT " " . $data . "\n";
3538 $count++;
3539 last if $num_rows && $count >= $num_rows;
3540 return(0) if $sigintcaught; #pseudo sig handle
3543 return(0) if $sigintcaught; #pseudo sig handle
3545 # If we didn't get any rows back, then the query was probably an insert or
3546 # update, so we call format_affected
3547 if($count <= 0 && !$isselect) {
3548 return($sth->rows(), 'affected');
3551 return($count, 'selected');
3553 } elsif($format eq 'single_output') {
3554 # Outputs a single return column/row without any labeling
3556 print FOUT "\n";
3558 my $res = $sth->fetchrow_array();
3559 print FOUT "$res\n";
3561 my $count = ($res ? 1 : 0);
3563 return(0) if $sigintcaught; #pseudo sig handle
3565 return($count, 'selected');
3567 } elsif($format eq 'csv' || $format eq 'csv_no_header') {
3568 # output in a comma seperated values format. fields with a ',' are quoted
3569 # with '"' quotes, and rows are seperated by '\n' newlines
3571 print "\r\e[K" if $op eq '<';
3572 print FOUT "\n";
3574 # check that Text::CSV_XS was included ok, if not output an error
3575 if($notextcsv) {
3576 soft_err("You must install Text::CSV_XS from CPAN to use this feature");
3577 return(0);
3578 } else {
3579 my $fields = $sth->{NAME};
3581 if($format eq 'csv') {
3582 # Print the column headers
3583 for(my $i = 0; $i < @$fields; $i++) {
3584 print FOUT "," if $i > 0;
3585 print FOUT $fields->[$i];
3587 print FOUT "\n";
3590 my $count = 0;
3591 while(my $res = $sth->fetch()) {
3592 $count++;
3594 $csv->combine(@$res);
3595 print FOUT $csv->string() . "\n";
3597 last if $num_rows && $count >= $num_rows;
3598 return(0) if $sigintcaught; #pseudo sig handle
3601 return(0) if $sigintcaught; #pseudo sig handle
3603 # If we didn't get any rows back, then the query was probably an insert or
3604 # update, so we call format_affected
3605 if($count <= 0 && !$isselect) {
3606 return($sth->rows(), 'affected');
3609 return($count, 'selected');
3611 } elsif($format eq 'sql') {
3612 # Produce SQL insert statements.
3613 print "\r" if $op eq '<';
3614 print FOUT "\n";
3616 my $cols = lc join(', ', @{$sth->{NAME}});
3617 my @types = map { scalar $dbh->type_info($_)->{TYPE_NAME} } @{ $sth->{TYPE} };
3618 my %warned_unknown_type;
3620 my $count = 0;
3621 while(my $res = $sth->fetch()) {
3622 $count++;
3623 die if @$res != @types;
3624 print FOUT "insert into TABLE ($cols) values (";
3625 foreach (0 .. $#$res) {
3626 my $t = $types[$_];
3627 my $v = $res->[$_];
3628 if (not defined $v) {
3629 print FOUT 'null';
3630 } else {
3631 if ($t eq 'DOUBLE' or $t eq 'DOUBLE PRECISION' or
3632 $t eq 'NUMBER' or $t eq 'DECIMAL') {
3633 die "bad number: $v" if $v !~ /\d/;
3634 print FOUT $v;
3635 } elsif ($t eq 'VARCHAR2' or $t eq 'CHAR' or $t eq 'CLOB') {
3636 $v =~ s/['']/''/g;
3637 print FOUT "'$v'";
3638 } elsif ($t eq 'DATE') {
3639 print FOUT "'$v'";
3640 } else {
3641 warn "don't know how to handle SQL type $t"
3642 unless $warned_unknown_type{$t}++;
3643 print FOUT "(unknown type $t: $v)";
3646 print FOUT ', ' unless $_ eq $#$res;
3648 print FOUT ");\n";
3649 last if $num_rows && $count >= $num_rows;
3650 return(0) if $sigintcaught; #pseudo sig handle
3652 return(0) if $sigintcaught; #pseudo sig handle
3654 # If we didn't get any rows back, then the query was probably an insert or
3655 # update, so we call format_affected
3656 if($count <= 0 && !$isselect) {
3657 return($sth->rows(), 'affected');
3659 return($count, 'selected');
3660 } else {
3661 die("Invalid format: $format");
3665 sub format_affected {
3666 my($rows_affected, $success_code) = @_;
3667 debugmsg(3, "format_affected called", @_);
3668 # This just outputs the given number
3670 return("$rows_affected row" . ($rows_affected == 1 ? '' : 's')
3671 ." $success_code");
3674 sub statusline {
3675 my($num, $max) = @_;
3676 debugmsg(3, "statusline called", @_);
3677 my $linewidth;
3678 eval q{
3679 use Term::ReadKey;
3680 (\$linewidth) = GetTerminalSize();
3682 if($@) {
3683 $linewidth = 80;
3685 my $numwidth = length($num);
3686 my $maxwidth = length($max);
3687 my $width = $linewidth - $numwidth - $maxwidth - 9;
3689 my $fillnum = (($num / $max) * $width);
3690 my $spacenum = ((($max - $num) / $max) * $width);
3692 if($fillnum =~ /\./) {
3693 $fillnum = int($fillnum) + 1;
3696 if($spacenum =~ /\./) {
3697 $spacenum = int($spacenum);
3700 my $fill = ('*' x $fillnum);
3701 my $space = ('-' x $spacenum);
3702 my $pcnt = sprintf("%.0d", ($num / $max * 100));
3704 return(sprintf("%-" . $linewidth . "s", "$num/$max [" . $fill . $space . "] $pcnt\%") . "\r");
3707 sub statusprint {
3708 my($string) = @_;
3710 return("\r\e[K$string\n");
3713 sub ping {
3714 debugmsg(3, "ping called", @_);
3715 if(!$dbh) {
3716 return(0);
3717 } else {
3718 # install alarm signal handle
3719 $SIG{ALRM} = \&sighandle;
3720 debugmsg(2, "Setting alarm for ping ($conf{connection_timeout} seconds)");
3721 alarm($conf{connection_timeout});
3723 debugmsg(2, "Pinging...");
3724 if($dbh->ping()) {
3725 debugmsg(2, "Ping successfull");
3726 alarm(0); # cancel alarm
3727 return(1);
3728 } else {
3729 debugmsg(2, "Ping failed");
3730 alarm(0); # cancel alarm
3731 db_reconnect();
3732 return(0);
3735 alarm(0); # cancel alarm
3738 sub query_err {
3739 my($query_type, $msg, $query) = @_;
3740 debugmsg(3, "query_err called", @_);
3741 # outputs a standard query error. does not exit
3742 # input: $query_type, $msg, $query
3744 chomp($query_type);
3745 chomp($msg);
3746 chomp($query);
3748 print STDERR "\n";
3749 print STDERR "$msg\n";
3750 print STDERR "Query: $query\n" if $query && $conf{sql_query_in_error};
3751 print STDERR "\n";
3754 sub lerr {
3755 my($msg) = @_;
3756 debugmsg(3, "err called", @_);
3757 # outputs an error message and exits
3759 print "Error: $msg\n";
3760 quit(1);
3763 sub soft_err {
3764 my($msg) = @_;
3765 debugmsg(3, "soft_err called", @_);
3766 # outputs a error, but doesn't exit
3768 print "\nError: $msg\n\n";
3771 sub wrn {
3772 my($msg) = @_;
3773 debugmsg(3, "wrn called", @_);
3774 # outputs a warning
3776 print STDERR "Warning: $msg\n";
3779 sub quit {
3780 my($exitcode, $force_quit, $msg) = @_;
3781 debugmsg(3, "quit called", @_);
3782 # just quits
3783 $exitcode ||= 0;
3784 $force_quit ||= 0; # Set this to 1 to try a smoother force quit
3785 $msg ||= '';
3787 setup_sigs();
3789 print "$msg" if $msg && $msg != "";
3790 $quitting = 1;
3792 if($force_quit) {
3793 exit($exitcode);
3796 commit_on_exit();
3798 # disconnect the database
3799 debugmsg(1, "disconnecting from database");
3800 if (defined $dbh) {
3801 $dbh->disconnect()
3802 or warn "Disconnect failed: $DBI::errstr\n";
3805 debugmsg(1, "exiting with exitcode: [$exitcode]");
3806 exit($exitcode);
3809 sub commit_on_exit {
3810 debugmsg(3, "commit_on_exit called", @_);
3812 # Commit... or not
3813 if($conf{commit_on_exit} && defined $dbh && !$dbh->{AutoCommit}) {
3814 # do nothing, oracle commits on disconnect
3815 } elsif(defined $dbh && !$dbh->{AutoCommit}) {
3816 print "Rolling back any outstanding transaction...\n";
3817 $dbh->rollback()
3818 or warn "Rollback failed: $DBI::errstr\n";
3822 sub debugmsg {
3823 my($debuglevel, @msgs) = @_;
3824 if($opt_debug >= $debuglevel ) {
3825 my @time = localtime();
3826 my $time = sprintf("%.4i-%.2i-%.2i %.2i:%.2i:%.2i", $time[5] + 1900,
3827 $time[4] + 1, $time[3], $time[2], $time[1], $time[0]);
3828 print STDERR "$time $debuglevel [" . join("] [", @msgs) . "]\n";
3832 sub usage {
3833 my($exit) = @_;
3834 debugmsg(3, "usage called", @_);
3836 $exit ||= 0;
3838 print <<_EOM_;
3839 Usage: yasql [options] [logon] [AS {SYSDBA|SYSOPER}] [@<file>[.ext]
3840 [<param1> <param2> ...]]
3841 Logon: <username>[/<password>][@<connect_string>] | /
3842 Options:
3843 -d, --debug=LEVEL Turn debugging on to LEVEL
3844 -H, --host=HOST Host to connect to
3845 -p, --port=PORT Host port to connect to
3846 -s, --sid=SID Oracle SID to connect to
3847 -h, -?, --help This help information
3848 -A, --nocomp Turn off building the auto-completion list
3849 -b, --bench, --benchmark Display extra benchmarking info
3850 -v, --version Print version and exit
3851 -B, --batch Batch mode (no headers, etc.)
3853 See the man pages for more help.
3854 _EOM_
3856 exit($exit);
3859 sub help {
3860 debugmsg(3, "help called", @_);
3861 # This just outputs online help
3863 my $help = <<_EOM_;
3865 Commands:
3866 help This screen
3867 quit, exit, \\q Exit the program.
3868 !<cmd>, host <cmd> Sends the command directly to a shell.
3869 \\A Regenerate the auto-completion list.
3870 connect [logon] [AS {SYSDBA|SYSOPER}]
3871 Open new connection.
3872 login = <username>[/<password>][@<connect_string>] | /
3873 reconnect, \\r Reconnect to the database
3874 desc[ribe] <object> Describe table, view, index, sequence, primary key,
3875 foreign key, constraint or trigger
3876 object = [<schema>.]<object>[\@dblink]
3877 show [all] <string> { like <name> }
3878 Shows [all] objects of a certain type
3879 string = tables, views, objects, sequences, clusters,
3880 dimensions, functions, procedures, packages,
3881 indexes, indextypes, libraries, snapshots,
3882 materialized views, synonyms, triggers,
3883 constraints
3884 name : use % for wildcard
3885 show <string> on|for <object>
3886 Shows properties for a particular object
3887 string = indexes, constraints, keys, checks, triggers,
3888 query, deps, ddl
3889 show processes Shows logged in users
3890 show [all] waits Shows [all] waits
3891 show plan Shows the last EXPLAIN PLAN ran
3892 show errors Shows errors from PL/SQL object creation
3893 l[ist], \\l, \\p List the contents of the current buffer
3894 cl[ear] [buffer], \\c
3895 Clear the current buffer
3896 ed[it] [filename], \\e [filename]
3897 Will open a text editor as defined by the EDITOR
3898 environment variable. If a file is given as the
3899 argument, then the editor will be opened with that
3900 file. If the given file does not exist then it will be
3901 created. In both cases the file will not be deleted,
3902 and the current buffer will be overwritten by the
3903 contents of the file. If no file is given, then the
3904 editor will be opened with a temporary file, which will
3905 contain the current contents of the buffer, or the last
3906 execute query if the buffer is empty. After the editor
3907 quits, the file will be read into the buffer. The
3908 contents will be parsed and executed just as if you had
3909 typed them all in by hand. You can have multiple
3910 commands and/or queries. If the last command is not
3911 terminated them you will be able to add furthur lines
3912 or input a terminator to execute the query.
3913 \@scriptname Execute all the commands in <filename> as if they were
3914 typed in directly. All CLI commands and queries are
3915 supported. yasql will quit after running all
3916 commands in the script.
3917 debug [num] Toggle debuggin on/off or if <num> is specified, then
3918 set debugging to that level
3919 autocommit Toggle AutoCommit on/off
3920 set <string> Set options
3921 string = [
3922 [long_read_len <size>]
3923 || [ fast_describe [on|off]]
3924 || [ serverout{put} [on|off] {size <size>} ]
3926 let <search string> Display all configurations
3928 Queries:
3929 All other input is treated as a query, and is sent straight to the database.
3931 All queries must be terminated by one of the following characters:
3932 ; - Returns data in table form
3933 / - Returns data in table form
3934 \\g - Returns data in non-aligned list form
3935 \\G - Returns data in aligned list form
3936 \\s - Returns data in CSV form. The first line is the column names
3937 \\S - Returns data in CSV form, but no column names
3938 \\i - Returns data in sql select commands form
3940 You may re-run the last query by typing the terminator by itself.
3942 Example:
3943 user\@ORCL> select * from table;
3944 user\@ORCL> \\g
3946 Return limit:
3947 You may add a number after the terminator, which will cause only the
3948 first <num> rows to be returned. e.g. 'select * from table;10' will run
3949 the query and return the first 10 rows in table format. This will also work
3950 if you just type the terminator to rerun the last query.
3952 Examples:
3953 The following will run the query, then run it again with different settings:
3954 user\@ORCL> select * from table;10
3955 user\@ORCL> \G50
3957 Redirection:
3958 You can add a shell like redirection operator after a query to pipe the output
3959 to or from a file.
3961 Output:
3962 You can use either '>' or '>>' to output to a file. '>' will overwrite the
3963 file and '>>' will append to the end of the file. The file will be created
3964 if it does not exist.
3966 Examples:
3967 user\@ORCL> select * from table; > table.dump
3968 user\@ORCL> select * from table\S > table.csv
3970 Input:
3971 You can use '<' to grab data from a CSV file. The file must be formatted
3972 with comma delimiters, quoted special fields, and rows seperated by
3973 newlines. When you use this operator with a query, the query will be ran
3974 for every line in the file. Put either '?' or ':n' (n being a number)
3975 placeholders where you want the data from the CSV file to be interpolated.
3976 The number of placeholders must match the number of columns in the CSV file.
3977 Each query is run as if you had typed it in, so the AutoCommit setting
3978 applies the same. If there is an error then the process will stop, but no
3979 rollback or anything will be done.
3981 Examples:
3982 user\@ORCL> insert into table1 values (?,?,?); < table1.csv
3983 user\@ORCL> update table2 set col1 = :1, col3 = :3, col2 = :2; < table2.csv
3985 Piping
3986 You can pipe the output from a query to the STDIN of any program you wish.
3988 Examples:
3989 user\@ORCL> select * from table; | less
3990 user\@ORCL> select * from table; | sort -n
3992 Please see 'man yasql' or 'perldoc yasql' for more help
3993 _EOM_
3995 my $ret = open(PAGER, "|$conf{pager}");
3996 if($ret) {
3997 print PAGER $help;
3998 close(PAGER);
3999 } else {
4000 print $help;
4004 __END__
4006 =head1 NAME
4008 yasql - Yet Another SQL*Plus replacement
4010 =head1 SYNOPSIS
4012 B<yasql> [options] [logon] [@<file>[.ext] [<param1> <param2>]
4014 =over 4
4016 =item logon
4018 <I<username>>[/<I<password>>][@<I<connect_string>>] | /
4020 =item options
4022 =over 4
4024 =item -d I<debuglevel>, --debug=I<debuglevel>
4026 Turn debuggin on to I<debuglevel> level. Valid levels: 1,2,3,4
4028 =item -H I<hostaddress>, --host=I<hostaddress>
4030 Host to connect to
4032 =item -p I<hostport>, --port=I<hostport>
4034 Host port to connect to
4036 =item -s I<SID>, --sid=I<SID>
4038 Oracle SID to connect to
4040 =item -h, -?, --help
4042 Output usage information and quit.
4044 =item -A, --nocomp
4046 Turn off the generation of the auto-completion list at startup. Use This if
4047 it takes too long to generate the list with a large database.
4049 =item -b, --bench, --benchmark
4051 Turn on extended benchmark info, which includes times and CPU usages for both
4052 queries and formatting.
4054 =item -v, --version
4056 Print version and exit
4058 =back
4060 =item Examples
4062 =over 4
4064 =item Connect to local database
4066 =over 4
4068 =item yasql
4070 =item yasql user
4072 =item yasql user/password
4074 =item yasql user@LOCAL
4076 =item yasql user/password@LOCAL
4078 =item yasql -h localhost
4080 =item yasql -h localhost -p 1521
4082 =item yasql -h localhost -p 1521 -s ORCL
4084 =back
4086 =item Connect to remote host
4088 =over 4
4090 =item yasql user@REMOTE
4092 =item yasql user/password@REMOTE
4094 =item yasql -h remote.domain.com
4096 =item yasql -h remote.domain.com -p 1512
4098 =item yasql -h remote.domain.com -p 1512 -s ORCL
4100 =back
4102 =back
4104 =back
4106 If no connect_string or a hostaddress is given, then will attempt to connect to
4107 the local default database.
4109 =head1 DESCRIPTION
4111 YASQL is an open source Oracle command line interface. YASQL features a much
4112 kinder alternative to SQL*Plus's user interface. This is meant to be a
4113 complete replacement for SQL*Plus when dealing with ad hoc queries and general
4114 database interfacing. It's main features are:
4116 =over 4
4118 =item Full ReadLine support
4120 Allows the same command line style editing as other ReadLine enabled programs
4121 such as BASH and the Perl Debugger. You can edit the command line as well as
4122 browse your command history. The command
4123 history is saved in your home directory in a file called .yasql_history. You
4124 can also use tab completion on all table and column names.
4126 =item Alternate output methods
4128 A different style of output suited to each type of need. There are currently
4129 table, list and CSV output styles. Table style outputs in the same manner as
4130 SQL*Plus, except the column widths are set based on the width of the data in
4131 the column, and not the column length defined in the table schema. List outputs
4132 each row on it's own line, column after column for easier viewing of wide return
4133 results. CSV outputs the data in Comma Seperated Values format, for easy
4134 import into many other database/spreadsheet programs.
4136 =item Output of query results
4138 You can easily redirect the output of any query to an external file
4140 =item Data Input and Binding
4142 YASQL allows you to bind data in an external CSV file to any query, using
4143 standard DBI placeholders. This is the ultimate flexibility when inserting or
4144 updating data in the database.
4146 =item Command pipes
4148 You can easily pipe the output of any query to an external program.
4150 =item Tab completion
4152 All tables, columns, and other misc objects can be completed using tab, much
4153 like you can with bash.
4155 =item Easy top rownum listings
4157 You can easily put a number after a terminator, which will only output those
4158 number of lines. No more typing "where rownum < 10" after every query. Now
4159 you can type 'select * from table;10' instead.
4161 =item Enhanced Data Dictionary commands
4163 Special commands like 'show tables', 'desc <table>', 'show indexes on <table>',
4164 'desc <sequence>', and many many more so that you can easily see your schema.
4166 =item Query editing
4168 You can open and edit queries in your favorite text editor.
4170 =item Query chaining
4172 You can put an abitrary number of queries on the same line, and each will be
4173 executed in turn.
4175 =item Basic scripting
4177 You can put basic SQL queries in a script and execute them from YASQL.
4179 =item Config file
4181 You can create a config file of options so that you don't have to set them
4182 everytime you run it.
4184 =item Future extensibility
4186 We, the community, can modify and add to this whatever we want, we can't do that
4187 with SQL*Plus.
4189 =back
4191 =head1 REQUIREMENTS
4193 =over 4
4195 =item Perl 5
4197 This was developed with Perl 5.6, but is known to work on 5.005_03 and above.
4198 Any earlier version of Perl 5 may or may not work. Perl 4 will definately not
4199 work.
4201 =item Unix environment
4203 YASQL was developed under GNU/Linux, and aimed at as many Unix installations as
4204 possible. Known to be compatible with GNU/Linux, AIX and Sun Solaris.
4205 Please send me an email (qzy@users.sourceforge.net) if it works for other platforms.
4206 I'd be especially interested if it worked on Win32.
4208 =item Oracle Server
4210 It has been tested and developed for Oracle8 and Oracle8i. There is atleast
4211 one issue with Oracle7 that I know of (see ISSUES below) and I have not tested
4212 it with Oracle9i yet.
4214 =item Oracle client libraries
4216 The Oracle client libraries must be installed for DBD::Oracle. Of course you
4217 can't install DBD::Oracle without them...
4219 =item DBD::Oracle
4221 DBD::Oracle must be installed since this uses DBI for database connections.
4223 =item ORACLE_HOME
4225 The ORACLE_HOME environment variable must be set if you use a connection
4226 descriptor to connect so that YASQL can translate the descriptor into
4227 usefull connection information to make the actual connection.
4229 =item ORACLE_SID
4231 The ORACLE_SID environment variable must be set unless you specify one with the
4232 -s option (see options above).
4234 =item Term::Readline
4236 Term::Readline must be installed (it is with most Perl installations), but more
4237 importantly, installing Term::ReadLine::Gnu from CPAN will greatly enhance the
4238 usability.
4240 =item Time::HiRes
4242 This is used for high resolution benchmarking. It is optional.
4244 =item Text::CSV_XS
4246 This perl module is required if you want to output CSV or input from CSV files.
4247 If you don't plan on using this features, then you don't need to install this
4248 module.
4250 =item Term::ReadKey
4252 This module is used for better input and output control. Right now it isn't
4253 required, but some parts of YASQL will look and function better with this
4254 installed.
4256 =back
4258 =head1 CONFIG
4260 YASQL will look for a config file first in ~/.yasqlrc then
4261 /etc/yasql.conf. The following options are available:
4263 =over 4
4265 =item connection_timeout = <seconds>
4267 Timeout for connection attempts
4269 Default: 20
4271 =item max_connection_attempts = <num>
4273 The amount of times to attempt the connection if the username/password are wrong
4275 Default: 3
4277 =item history_file = <file>
4279 Where to save the history file. Shell metachars will be globbed (expanded)
4281 Default: ~/.yasql_history
4283 =item pager = <file>
4285 Your favorite pager for extended output. (right now only the help command)
4287 Default: /bin/more
4289 =item auto_commit = [0/1]
4291 Autocommit any updates/inserts etc
4293 Default: 0
4295 =item commit_on_exit = [0/1]
4297 Commit any pending transactions on exit. Errors or crashes will still cause
4298 the current transaction to rollback. But with this on a commit will occur
4299 when you explicitly exit.
4301 Default: 0
4303 =item long_trunc_ok = [0/1]
4305 Long truncation OK. If set to 1 then when a row contains a field that is
4306 set to a LONG time, such as BLOB, CLOB, etc will be truncated to long_read_len
4307 length. If 0, then the row will be skipped and not outputted.
4309 Default: 1
4311 =item long_read_len = <num_chars>
4313 Long Read Length. This is the length of characters to truncate to if
4314 long_trunc_ok is on
4316 Default: 80
4318 =item edit_history = [0/1]
4320 Whether or not to put the query edited from the 'edit' command into the
4321 command history.
4323 Default: 1
4325 =item auto_complete = [0/1]
4327 Whether or not to generate the autocompletion list on connection. If connecting
4328 to a large database (in number of tables/columns sense), the generation process
4329 could take a bit. For most databases it shouldn't take long at all though.
4331 Default: 1
4333 =item extended_complete_list = [0/1]
4335 extended complete list will cause the possible matches list to be filled by
4336 basicly any and all objects. With it off the tab list will be restricted to
4337 only tables, columns, and objects owned by the current user.
4339 Default: 0
4341 =item complete_tables = [0/1]
4343 This controls whether or not to add tables to the completion list. This does
4344 nothing if auto_complete is set to 0.
4346 Default: 1
4348 =item complete_columns = [0/1]
4350 This controls whether or not to add columns to the completion list. This does
4351 nothing if auto_complete is set to 0.
4353 Default: 1
4355 =item complete_objects = [0/1]
4357 This controls whether or not to add all other objects to the completion list.
4358 This does nothing if auto_complete is set to 0. (Hint... depending on your
4359 schema this will include tables and columns also, so you could turn the other
4360 two off)
4362 Default: 1
4364 =item extended_benchmarks = [0/1]
4366 Whether or not to include extended benchmarking info after queries. Will
4367 include both execution times and CPU loads for both the query and formatting
4368 parts of the process.
4370 Default: 0
4372 =item prompt
4374 A string to include in the prompt. The prompt will always be suffixed by a
4375 '>' string. Interpolated variables:
4376 %H = connected host. will be prefixed with a '@'
4377 %U = current user
4379 Default: %U%H
4381 =item column_wildcards = [0/1]
4383 Column wildcards is an extremely experimental feature that is still being
4384 hashed out due to the complex nature of it. This should affect only select
4385 statements and expands any wildcards (*) in the column list. such as
4386 'select col* from table;'.
4388 Default: 0
4390 =item sql_query_in_error = [0/1]
4392 This this on to output the query in the error message.
4394 Default: 0
4396 =item nls_date_format = <string>
4398 Set the preferred NLS_DATE_FORMAT. This effects both date input and output
4399 formats. The default is ISO standard (YYYY-MM-DD HH24:MI:SS', not oracle
4400 default (YYYY-MM-DD).
4402 Default: YYYY-MM-DD HH24:MI:SS
4404 =item fast_describe
4406 Turn on fast describes. These are much faster than the old style of desc
4407 <table>, however non-built in datatypes may not be returned properly. i.e. a
4408 FLOAT will be returned as a NUMBER type. Internally FLOATs really are just
4409 NUMBERs, but this might present problems for you. If so, set this to 0
4411 Default: 1
4413 =back
4415 =head1 ISSUES
4417 =over 4
4419 =item Oracle7
4421 DBD::Oracle for Oracle8 may have issues connecting to an Oracle7 database. The
4422 one problem I have seen is that the use of placeholders in a query will cause
4423 oracle to issue an error "ORA-01008: not all variables bound". This will affect
4424 all of the hard-coded queries that I use such as the ones for the 'desc' and
4425 'show' commands. The queries that you type in on the command line may still
4426 work. The DBD::Oracle README mentions the use of the '-8' option to the
4427 'perl Makefile.PL' command to use the older Oracle7 OCI. This has not been
4428 tested.
4430 =back
4432 =head1 AUTHOR
4434 Originaly written by Nathan Shafer (B<nshafer@ephibian.com>) with support from
4435 Ephibian, Inc. http://www.ephibian.com
4436 Now it is mostly developed and maintained by Balint Kozman
4437 (B<qzy@users.sourceforge.net>). http://www.imind.hu
4439 =head1 THANKS
4441 Thanks to everyone at Ephibian that helped with testing, and a special thanks
4442 to Tom Renfro at Ephibian who did a lot of testing and found quite a few
4443 doozies.
4444 Also a lot of thanks goes to the mates at iMind.dev who keep suffering from
4445 testing new features on them.
4447 The following people have also contributed to help make YASQL what it is:
4448 Allan Peda, Lance Klein, Scott Kister, Mark Dalphin, Matthew Walsh
4450 And always a big thanks to all those who report bugs and problems, especially
4451 on other platforms.
4453 =head1 COPYRIGHT
4455 Copyright (C) 2000-2002 Ephibian, Inc., 2005 iMind.dev.
4458 =head1 LICENSE
4460 This program is free software; you can redistribute it and/or
4461 modify it under the terms of the GNU General Public License
4462 as published by the Free Software Foundation; either version 2
4463 of the License, or (at your option) any later version.
4465 This program is distributed in the hope that it will be useful,
4466 but WITHOUT ANY WARRANTY; without even the implied warranty of
4467 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4468 GNU General Public License for more details.
4470 You should have received a copy of the GNU General Public License
4471 along with this program; if not, write to the Free Software
4472 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
4474 =head1 TODO
4476 =over 4
4478 =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
4480 =item allow history to be saved based on host (as an option)
4482 =item make stifle_history a configurable option
4484 =item a row is printed after "Attempting to cancel query"
4486 =item reading from a script will not change prompt properly (for a script with no terminator)
4488 =item NULL stops printing after table goes into overflow or something
4490 =item extra space in \G... maybe others
4492 =item bug: tag completion doesn't work with caps anymore
4494 =item Add support for /NOLOG
4496 =item allow dblinks in show blah on blah commands
4498 =item show query doesn't work with schemas and db links
4500 =item add save and get buffer commands
4502 =item add R[UN] command (/ equivilent)
4504 =item add support for just 'connect' and prompt for username and password
4506 =item add PASSW[ORD] command for changing password
4508 =item add -s[ilent] command line to suppress all startup output and command prompts
4510 =item add 'start' command for scripting
4512 =item add 'run' synonum for '/'
4514 =item add 'show parameters <filter>' support
4516 =item fix segfaults when cancelling large outputs
4518 =item Add a 'SPOOL' command
4520 =item fix 'set...' commands
4522 =item Add variable bindings, prompting, control structures, etc.
4524 =item be able to describe any kind of object
4526 =item Add 'startup queries' in config file or support glogin.sql and login.sql
4528 =item fix case sensitive object names
4530 =item make win32 compliant
4532 =item add better error messages when the user can't access a data dictionary
4533 table
4535 =item add better error output, with line/col numbers and maybe a pointer.
4537 =item add chained ops, exactly like bash
4539 =item add plugins and hooks for all aspects.
4541 =item Add smarter tables and wrapping in columns. Also add configurable max
4542 column widths and max table width.
4544 =item Add a curses interface option for easy viewing and scrolling, etc. This
4545 will require some research to determine if it's even worth it.
4547 =item Add HTML output option
4549 =back
4551 =head1 CHANGELOG
4553 $Log: yasql,v $
4554 Revision 1.83 2005/05/09 16:57:13 qzy
4555 Fixed the 'DECIMAL' problem with describe command.
4556 Added sql mode with \i (patch by Ed Avis).
4557 Added redirectors (>, >>, |) to describe.
4558 Added 'show user' command.
4559 Added 'show uid' command.
4560 Added new makefile targets: clean, check. (patch by Ed Avis)
4561 Added "and owner = ?" to some show targets (patch by anonymous).
4562 Added command_complete_list feature and config option.
4563 Added disconnect command
4564 Added command completion: select, update, insert, delete, execute, etc.
4565 Added table.column name completion.
4566 Added feature to run tty-less (patch by Michael Kroell).
4567 Added a workaround for SunOS's alarm() bug (patch by Ed Avis).
4568 Fixed some minor issues in parser code.
4570 Revision 1.82 2005/02/18 16:57:13 qzy
4571 Added batch mode (ewl patch).
4572 Allow connections AS SYSDBA, AS SYSOPER and internal (sysdba patch by Derek Whayman).
4573 Added server_output to config options.
4574 Changed script execution to only add script lines to the query buffer (and not to history).
4576 Revision 1.81 2002/03/06 21:55:13 nshafer
4577 Fixed bug with password prompt.
4578 Added 'show plan' for outputting last explain plan results.
4579 Added 'show query' for viewing queries for views and materialized views.
4580 Optimized describes to be as fast as describes in SQL*Plus.
4581 Added new option 'fast_describe' on by default for new describe method.
4582 Added single_output as a formatting option for internal use.
4583 Fixed problem with password, quit, exit, \q getting added to the history list.
4584 Changed history to not add duplicate entries right next to each other.
4585 Added support for basic (non-returning) PL/SQL commands.
4586 Added support for create function, package, package body, prodedure, trigger.
4587 Added 'show errors' command
4588 Added 'conn' shortcut for 'connection'.
4589 Added 'exec[ute]' command.
4590 Added 'set serverout[put] on|off' command to mimic SQL*Plus's.
4591 Added alarms to pings in cases where DB connection is dropped and ping hangs.
4592 Cleaned up error messages.
4593 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.
4594 Changed quote escaping to be '' and "" instead of \' and \".
4595 Added full support for comments: rem[ark], --, and /* */.
4596 Right-justify works for the '8' datatype as well as '3' now.
4597 Re-worked debug output levels.
4598 Optimized query for completion lists a bit.
4599 Added completion-list limiting based on location in some DML statements (select, update, insert).
4600 Fixed up the display of '...' when generating tab completion list. Should work a lot better when hitting tab in the middle of the line.
4601 Added show views, objects, sequences, clusters, dimensions, functions, procedures, packages, indexes, indextypes, libraries, materialized views, snapshots, synonyms, triggers.
4602 Added show all <objects> command.
4603 Added type and owner columns to show commands.
4604 Fixed commit_on_exit logic.
4605 Added ability to use external authentication ('yasql /').
4606 The .sql extension for the scripting and editing commands are now optional.
4607 Fixed up editor execution to hopefully find the editor better.
4608 Added "Command" entry to "show processes".
4609 Added "show waits" and "show all waits" commands.
4610 Re-organized command line usage in anticipation for script parameters.
4611 Removed all uses of 'stty'.
4612 Added processing of STDIN, so redirects and pipes to YASQL work now.
4613 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
4614 Updated documentation.
4615 Fixed up alarm() calls.
4616 Fixed setting of NLS_DATE_FORMAT to apply on reconnects.
4617 Broke commands into 2 sets... ones that exectute any time, and ones that execute only when nothing is in the buffer
4618 Fixed printing of text read in from an edit command. It now echoes all of it.
4619 Now ignoring most SET commands so we don't tack them onto queries
4620 Fixed permissions in tarball
4622 Revision 1.80 2001/08/01 18:06:27 nshafer
4623 Fixed bug with delayed $term initialization\e\b
4625 Revision 1.79 2001/08/01 17:52:35 nshafer
4626 Fixed compatibility issues with the data dictionary in Oracle 7. Fixed ordering
4627 of indexes for compound indexes. Fixed display of objects from other schemas
4628 in some data dictionary commands such as 'show indexes on table'. (Thanks Nix)
4629 Fixed matching of declare and end in query string. Will not only match if on
4630 blank line. Fixed matching of '/' terminator in middle of queries. Will now
4631 only match if at end of line (Thanks Wesley Hertlein). Temp file for editing
4632 now appends '.sql' to end of temp file so that editors, like vim, automatically
4633 turn on syntax highlighting. Added searching of environment variable SQLPATH
4634 when looking for scripts. Terminal setup is now after script parsing, so that
4635 it will work when run under cron (Thanks David Zverina).
4637 Revision 1.78 2001/07/05 13:52:56 nshafer
4638 Fixed bug where parens were matching improperly.
4640 Revision 1.77 2001/07/04 02:57:08 nshafer
4641 Fixed bug where terminators wouldn't match if they were the next character
4642 after a quote character.
4644 Revision 1.76 2001/06/28 04:17:53 nshafer
4645 Term::ReadLine::Perl now supported, for what little functionality it does
4646 provide. Fixed segfault when hitting up when history is empty. Fixed bug
4647 when providing script names on command line (Thanks to Dave Zverina.)
4648 Rewrote the query parser to fix a bug, caused by the multiple-queries-on-one-
4649 line feature, that causes terminators, such as ';' and '/' to match when in
4650 quotes. When hitting tab on a line starting with a '@' for scripts, tab will
4651 now complete filenames and not database objects. Fixed DB timeout when
4652 prompting for username and password. Added support for 'DECLARE' keyword,
4653 however this does not mean that variable binding in PL/SQL blocks works yet.
4654 Sped up startup time a bit more (hopefully).
4656 Revision 1.75 2001/06/19 16:02:16 nshafer
4657 Fixed typo in error message for Term::ReadLine::Gnu
4658 Fixed crash when tab hit at username or password prompt
4659 Added -- as a comment type and fixed case where comment in quotes would
4660 match. (Mark Dalphin)
4661 Fixed 'desc' to also describe partitioned tables (Erik)
4663 Revision 1.74 2001/06/18 21:07:55 nshafer
4664 Fixed bug where / would not rerun last query (thanks Scott Kister)
4666 Revision 1.73 2001/05/23 18:35:17 nshafer
4667 Got rid of "Prototype mismatch" errors. Fixed typo in extended benchmarks
4669 Revision 1.72 2001/05/22 16:06:36 nshafer
4670 Fixed bug with error messages not displaying first time, and fixed bug with
4671 tab completion output
4673 Revision 1.71 2001/05/17 21:28:40 nshafer
4674 New CSV output format. Added CSV file input on any query. Added ability to
4675 pipe query results to any program. Added ability for multiple queries on one
4676 line. Changed tab completion generator to run first time you hit tab instead
4677 of on startup, which speeds up database connection. Now using SelfLoader to
4678 speed up loading and minimize memory use. Added a 'show plan for ____' command
4679 for easy display of explain plan output. Query times are now more readable
4680 and will split into weeks, days, hours, minutes, and seconds. Hopefully fixed
4681 some problems with stty and Solaris 2.4. Added support for 'rem' comments in
4682 scripts. Redirection output files are now shell expanded.
4684 Revision 1.70 2001/05/08 17:49:51 nshafer
4685 Fixed all places where a non-alphanumeric object name would break or not
4686 match.
4687 Added code for autoconf style installs.
4689 Revision 1.69 2001/05/07 23:47:47 nshafer
4690 fixed type
4692 Revision 1.68 2001/05/07 22:26:20 nshafer
4693 Fixed tab completion problems when completing objects with a $ in their name.
4694 Added config options complete_tables, complete_columns, and complete_objects,
4695 Added redirection of query output to file. Hopefully sped up exiting.
4696 Updated documentation.
4698 Revision 1.67 2001/05/04 17:35:04 nshafer
4699 YASQL will now suspend properly back to the shell when SIGTSTP is sent, as in
4700 when you hit ctrl-z on most systems. Added NLS_DATE_FORMAT setting in config
4701 file to support alter date views. Defaults to ISO standard. YASQL will now
4702 attempt to change it's process name, such as when viewed in ps or top. This
4703 will not work on all systems, nor is it a complete bullet proof way to hide
4704 your password if you provide it on the command line. But it helps to not
4705 make it so obvious to regular users. Scripts entered on the command line are
4706 now checked to be readable before attempting connection. A failed 'connect
4707 command will no long alter the prompt. Added \p option for printing the
4708 current buffer, ala psql. Large query results (over 1000 rows) are now
4709 handled MUCH better. YASQL will no longer try to hold more than 1000 rows in
4710 memory, which keeps it from sucking memory, and also improves the speed.
4711 When a query does return more than 1000 rows in table mode, those first 1000
4712 will determine the column widths, and all rows after that will get truncated.
4713 AIX has been reported to run YASQL perfectly.
4715 Revision 1.66 2001/03/13 21:34:58 nshafer
4716 There are no longer any references to termcap, so yasql should now work on
4717 termcap-less systems such as Debian Linux and AIX
4719 Revision 1.65 2001/03/12 17:44:31 nshafer
4720 Restoring the terminal is hopefully more robust and better now. YASQL now
4721 tries to use the 'stty' program to dump the settings of the terminal on
4722 startup so that it can restore it back to those settings. It requires that
4723 stty is installed in the path, but that should be the case with most systems.
4724 Also made the output of the query in the error message an option that is off
4725 by default. I had never meant to include that in the final release, but kept
4726 on forgetting to take it out.
4728 Revision 1.64 2001/03/06 16:00:33 nshafer
4729 Fixed bug where desc would match anytime, even in middle of query, which is
4730 bad.
4732 Revision 1.63 2001/03/01 17:30:26 nshafer
4733 Refined the ctrl-c process for not-so-linuxy OS's, namely solaris. Now
4734 stripping out Dos carriage returns since SQL*Plus seems to.
4736 Revision 1.62 2001/02/26 22:39:12 nshafer
4737 Fixed bug where prompt would reset itself when a blank line was entered.
4738 Added script argument on command line (Lance Klein)
4739 Added support for any command line commands in the script (Lance Klein)
4740 The 'desc' and 'show' commands no longer require a terminator (like ;) as long as the whole statement is on one line (Lance Klein)
4741 Added option 'extended_tab_list' for a much bigger, more complete tab listing (Lance Klein)
4742 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.
4743 cleaned up documentation a bit
4745 Revision 1.61 2001/01/31 19:56:22 nshafer
4746 changed CommitOnExit to be 1 by default, to emulate SQL*Plus behavior, and
4747 at popular request
4749 Revision 1.60 2001/01/29 16:38:17 nshafer
4750 got rid of (tm)
4752 Revision 1.59 2001/01/29 16:28:22 nshafer
4753 Modified docs a little with the new scope of open source now in the mix.
4755 Revision 1.58 2001/01/24 15:27:00 nshafer
4756 cleanup_after_signals is not in the Term::ReadLine::Stub, so it would
4757 output error messages on systems without Term::ReadLine::Gnu. Fixed
4759 Revision 1.57 2001/01/17 23:26:53 nshafer
4760 Added Tom Renfro's column_wildcard expansion code. New conf variable:
4761 column_wildcards. 0 by default until this code is expanded on a bit more.
4763 Revision 1.56 2001/01/17 23:00:25 nshafer
4764 Added CommitOnExit config, 0 by default. Added info output at startup and
4765 when a new connection is initiated about the state of AutoCommit and
4766 CommitOnExit. Also added statement about explicit rollback or commit when
4767 disconnecting. Added warning message to commit_cmd and rollback_cmd if
4768 AutoCommit is on. Now explicitly committing or rolling back on disconnect,
4769 it is no longer left up to the DBI's discretion... except in abnormal
4770 termination.
4772 Revision 1.55 2001/01/11 18:05:12 nshafer
4773 Added trap for regex errors in tab completion (like if you put 'blah[' then
4774 hit tab)
4776 Revision 1.54 2001/01/10 17:07:22 nshafer
4777 added output to those last 2 commands
4779 Revision 1.53 2001/01/10 17:03:58 nshafer
4780 added commit and rollback commands so that you don't have to send them to the
4781 backend
4783 Revision 1.52 2001/01/10 16:00:08 nshafer
4784 fixed bug with prompt where on each call get_prompt would add another '@'.
4785 Thanks Tom
4787 Revision 1.51 2001/01/09 21:16:12 nshafer
4788 dar... fixed another bug where the %H would stay if there was no prompt_host
4790 Revision 1.50 2001/01/09 21:12:13 nshafer
4791 fixed bug with that last update. Now it only interpolates the %H variable
4792 if there is something to interpolate it with
4794 Revision 1.49 2001/01/09 21:09:56 nshafer
4795 changed the %H variable to be prefixed with a @
4797 Revision 1.48 2001/01/09 21:04:36 nshafer
4798 changed 'default' to '' for the prompt's hostname when no connect_string is
4799 used
4801 Revision 1.47 2001/01/09 20:55:11 nshafer
4802 added configurable prompt and changed the default prompt
4804 Revision 1.46 2001/01/09 18:50:50 nshafer
4805 updated todo list
4807 Revision 1.45 2001/01/09 18:32:35 nshafer
4808 Added 'connect <connect_string>' command. I may add the ability to specify
4809 options like on the command line (like '-H blah.com')
4811 Revision 1.44 2001/01/08 22:08:49 nshafer
4812 more documentation changes
4814 Revision 1.43 2001/01/08 20:51:31 nshafer
4815 added some documentation
4817 Revision 1.42 2001/01/08 20:09:35 nshafer
4818 Added debug and autocommit commands
4820 Revision 1.41 2001/01/08 18:12:43 nshafer
4821 added END handler to hopefully clean up the terminal better
4823 Revision 1.40 2001/01/05 23:29:38 nshafer
4824 new name!
4826 Revision 1.39 2001/01/05 18:00:16 nshafer
4827 Added config file options for auto completion generation and extended
4828 benchmark info
4830 Revision 1.38 2001/01/05 16:39:47 nshafer
4831 Fixed error where calling edit a second time would not open the file properly
4832 because of the way glob() works.
4834 Revision 1.37 2001/01/04 23:52:30 nshafer
4835 changed the version string to parse it out of the revision string (duh...)
4836 moved the prompting of username and password so that the check for the
4837 oracle_home variable happens before. Before if you didn't have the environment
4838 variable set then it will prompt you for username and password, then die
4839 with the error, which is annoying
4840 fixed the quit calls so taht they properly erase the quit line from the
4841 history. I had broken this a long time ago when I added the exit status
4842 param to the quit function
4843 Outputting in full table format (';' terminator) with a num_rows number
4844 (like ';100') would still cause the entire result set to be pulled into
4845 memory, which was really slow and could take a lot of memory if the table
4846 was large. Fixed it so that it only pulls in num_rows number of rows when
4847 using the digit option
4849 Revision 1.36 2000/12/22 22:12:18 nshafer
4850 fixed a wrong-quote-type in the debug messages
4852 Revision 1.35 2000/12/22 22:07:06 nshafer
4853 forgot version... you know the drill...
4855 Revision 1.34 2000/12/22 21:57:01 nshafer
4856 Added config file support, queries from the 'edit' command are now entered
4857 into the command history (configurable), cleaned up the SIGINT actions quite
4858 a bit so they should work better now, added LongReadLen and LongTruncOk
4859 options so that LONG columns types won't mess up, added the number after terminator
4860 feature to limit how many rows are returned.
4862 Revision 1.33 2000/12/20 22:56:03 nshafer
4863 version number.... again.... sigh
4865 Revision 1.32 2000/12/20 22:55:32 nshafer
4866 added todo item, now in rpms
4868 Revision 1.31 2000/12/20 17:07:52 nshafer
4869 added the reprompt for username/password on error 1005 null password given
4871 Revision 1.30 2000/12/20 17:04:18 nshafer
4872 Refined the shadow_redisplay stuff. Now I will only use my builtin function
4873 if the terminal type is set to "xterm" because that terminal type has a
4874 broken termcap entry. Also set it to not echo when entering password if
4875 Term::ReadLine::Gnu is not installed
4877 Revision 1.29 2000/12/20 15:47:56 nshafer
4878 trying a new scheme for the shadow_redisplay. Clear to EOL wasn't working
4879 Also fixed a few problems in the documentation
4882 Revision 1.28 2000/12/19 23:55:03 nshafer
4883 I need to stop forgetting the revision number...
4885 Revision 1.27 2000/12/19 23:48:49 nshafer
4886 cleaned up debugging
4888 Revision 1.26 2000/12/19 23:10:18 nshafer
4889 Lotsa new stuff... tab completion of table, column, and object names,
4890 improved signal handling, the edit command now accepts a filename parameter,
4891 new command 'show processes' which shows you info on who's connected,
4892 improved benchmark info, and a lot of other cleanup/tweaks
4894 Revision 1.25 2000/12/13 16:58:26 nshafer
4895 oops forgot documentation again
4897 Revision 1.24 2000/12/13 16:54:42 nshafer
4898 added desc <trigger>
4900 Revision 1.23 2000/12/12 17:52:15 nshafer
4901 updated todo list (oops, forgot)
4903 Revision 1.22 2000/12/12 17:51:39 nshafer
4904 added desc <index>
4906 Revision 1.21 2000/12/12 17:15:28 nshafer
4907 fixed bug when connecting using a host string (-H option)
4908 added a few more types to the 'show' and 'desc' commands
4910 Revision 1.20 2000/12/08 22:13:43 nshafer
4911 many little fixes and tweaks here and there
4913 Revision 1.19 2000/12/06 20:50:03 nshafer
4914 added scripting ability with "@<filename>" command
4915 changed all tabs to spaces!
4917 Revision 1.18 2000/12/06 19:30:38 nshafer
4918 added clear command
4919 refined connection process. if invalid username/password entered then prompt again
4921 Revision 1.17 2000/12/05 22:20:58 nshafer
4922 Tightened up outputs. Doesn't show column names if no rows selected, if
4923 it's not a select, then show number of rows affected
4925 Revision 1.16 2000/12/04 18:04:53 nshafer
4926 *** empty log message ***
4928 Revision 1.15 2000/12/04 18:03:14 nshafer
4929 fixed bug where the -H option was interpreted as -h or help. All command
4930 line options are now case sensitive
4932 Revision 1.14 2000/12/04 17:54:38 nshafer
4933 Added list command (and \l and l)
4935 Revision 1.13 2000/12/04 17:34:18 nshafer
4936 fixed a formatting issue if Time::HiRes isn't installed
4938 Revision 1.12 2000/12/04 17:29:41 nshafer
4939 Added benchmark options to view the extended benchmark info. Now it displays
4940 just the time in a more friendly format. The old style is only active if the
4941 benchmark option is specified.
4942 Cleaned up some formatting issues
4943 Brought the usage and POD documentation up to date
4944 Added some items to the TODO
4946 Revision 1.11 2000/11/30 22:54:38 nshafer
4947 Fixed bug with the edit command where if you were 'inquotes' then you would
4948 stay in quotes even after editing the file
4950 Revision 1.10 2000/11/30 22:01:38 nshafer
4951 Fixed bug where username and password were added to the command history.
4952 Set it so that the quit commands are not added to the command history either.
4953 Added the 'edit' command and modified it's todo list item, as well as added
4954 it to the 'help' command
4956 Revision 1.9 2000/11/29 17:55:35 nshafer
4957 changed version from .21 to 1.0 beta 9. I'll follow the revision numbers now
4959 Revision 1.8 2000/11/29 17:46:31 nshafer
4960 added a few items to the todo list
4962 Revision 1.7 2000/11/29 15:50:56 nshafer
4963 got rid of SID output at startup
4965 Revision 1.6 2000/11/29 15:49:51 nshafer
4966 moved revision info to $revision and added Id output
4968 Revision 1.5 2000/11/29 15:46:41 nshafer
4969 fixed revision number
4971 Revision 1.4 2000/11/29 15:44:23 nshafer
4972 fixed issue where environment variable ORACLE_SID overwrote explicit set
4973 on the command line. now whatever you put on the command line will overwrite
4974 the environment variable
4976 =cut