File Coverage

blib/lib/DBI/Shell.pm
Criterion Covered Total %
statement 423 792 53.4
branch 124 388 31.9
condition 25 109 22.9
subroutine 68 91 74.7
pod 1 3 33.3
total 641 1383 46.3


line stmt bran cond sub pod time code
1             package DBI::Shell;
2             # vim:ts=4:sw=4:ai:aw:nowrapscan
3              
4             =head1 NAME
5              
6             DBI::Shell - Interactive command shell for the DBI
7              
8             =head1 SYNOPSIS
9              
10             perl -MDBI::Shell -e shell [ [ []]]
11              
12             or
13              
14             dbish [ [ []]]
15             dbish --debug [ [ []]]
16             dbish --batch [ [ []]] < batch file
17              
18             =head1 DESCRIPTION
19              
20             The DBI::Shell module (and dbish command, if installed) provide a
21             simple but effective command line interface for the Perl L module.
22              
23             =cut
24              
25             ###
26             ### See TO DO section in the docs at the end.
27             ###
28              
29              
30 7     7   557802 BEGIN { require 5.004 }
31 7     7   216 BEGIN { $^W = 1 }
32              
33 7     7   39 use strict;
  7         12  
  7         199  
34 7     7   41 use vars qw(@ISA @EXPORT $VERSION $SHELL);
  7         12  
  7         497  
35 7     7   77 use Exporter ();
  7         20  
  7         162  
36 7     7   45 use Carp;
  7         13  
  7         3070  
37              
38             @ISA = qw(Exporter DBI::Shell::Std);
39             @EXPORT = qw(shell);
40              
41             our $VERSION = '11.96_03'; # TRIAL VERSION
42             $VERSION = eval $VERSION;
43              
44             sub new {
45 5     5 0 2929 my $class = shift;
46 5 50       81 my @args = @_ ? @_ : @ARGV;
47             #my $sh = bless {}, $class;
48 5         66 my $sh = $class->SUPER::new(@args);
49             # Load configuration files, system and user. The user configuration may
50             # over ride the system configuration.
51 5         43 my $myconfig = $sh->configuration;
52             # Save the configuration file for this instance.
53 5         15 $sh->{myconfig} = $myconfig;
54             # Pre-init plugins.
55 5         61 $sh->load_plugins($myconfig->{'plug-ins'}->{'pre-init'});
56             # Post-init plugins.
57             #$sh->SUPER::init(@args);
58 5         26 $sh->load_plugins($myconfig->{'plug-ins'}->{'post-init'});
59              
60             # do_format is already run in DBI::Shell::Base::new, but that is
61             # before the user config is loaded so we need to run it again if
62             # the user overrides the format.
63 5         42 $sh->do_format($sh->{format});
64 5         24 return $sh;
65             }
66              
67             sub shell {
68 0 0   0 0 0 my @args = @_ ? @_ : @ARGV;
69 0         0 $SHELL = DBI::Shell::Std->new(@args);
70 0         0 $SHELL->load_plugins;
71 0         0 $SHELL->run;
72             }
73              
74             sub run {
75 0     0 1 0 my $sh = shift;
76 0         0 die "Unrecognised options: @{$sh->{unhandled_options}}\n"
77 0 0       0 if @{$sh->{unhandled_options}};
  0         0  
78              
79             # Use valid "dbi:driver:..." to connect with source.
80 0         0 $sh->do_connect( $sh->{data_source} );
81              
82             #
83             # Main loop
84             #
85 0         0 $sh->{abbrev} = undef;
86 0         0 $sh->{abbrev} = Text::Abbrev::abbrev(keys %{$sh->{commands}});
  0         0  
87             # unless $sh->{batch};
88 0         0 $sh->{current_buffer} = '';
89 0         0 $sh->SUPER::run;
90              
91             }
92              
93              
94             # -------------------------------------------------------------
95             package DBI::Shell::Std;
96              
97 7     7   54 use vars qw(@ISA);
  7         12  
  7         1503  
98             @ISA = qw(DBI::Shell::Base);
99              
100             # XXX this package might be used to override commands etc.
101             sub do_connect {
102 9     9   3314 my $sh = shift;
103             $sh->load_plugins($sh->{myconfig}->{'plug-ins'}->{'pre-connect'})
104 9 100       70 if exists $sh->{myconfig}->{'plug-ins'}->{'pre-connect'};
105 9         62 $sh->SUPER::do_connect(@_);
106             $sh->load_plugins($sh->{myconfig}->{'plug-ins'}->{'post-connect'})
107 9 100       45 if exists $sh->{myconfig}->{'plug-ins'}->{'post-connect'};
108 9         56 return;
109             }
110              
111             sub init {
112 0     0   0 my $sh = shift;
113 0         0 return;
114             }
115              
116              
117             # -------------------------------------------------------------
118             package DBI::Shell::Base;
119              
120 7     7   60 use Carp;
  7         22  
  7         418  
121 7     7   5574 use Text::Abbrev ();
  7         353  
  7         184  
122 7     7   3544 use Term::ReadLine;
  7         19462  
  7         290  
123 7     7   49 use Getopt::Long 2.17; # upgrade from CPAN if needed: http://www.perl.com/CPAN
  7         76  
  7         241  
124 7     7   4194 use IO::File;
  7         53449  
  7         829  
125 7     7   61 use File::Spec ();
  7         14  
  7         121  
126 7     7   3881 use File::HomeDir ();
  7         40071  
  7         269  
127              
128 7     7   11437 use DBI 1.00 qw(:sql_types :utils);
  7         132605  
  7         3260  
129 7     7   4111 use DBI::Format;
  7         20  
  7         264  
130              
131 7     7   3241 use DBI::Shell::FindSqlFile;
  7         16  
  7         299  
132 7     7   3543 use IO::Interactive qw/ is_interactive /;
  7         6584  
  7         52  
133              
134 7     7   363 use vars qw(@ISA);
  7         15  
  7         357  
135             @ISA = qw(DBI::Shell::FindSqlFile);
136              
137 7     7   43 use constant ADD_RH => 1; # Add the results, to rhistory.
  7         12  
  7         514  
138 7     7   46 use constant NO_RH => 0; # Do not add results, to rhistory.
  7         12  
  7         346  
139              
140             # History saving is also provided by DBI::Shell::Completion.
141 7     7   85 use constant HISTORY_FILE => '.dbish-builtin-history';
  7         16  
  7         68913  
142              
143             my $haveTermReadKey;
144             my $term;
145              
146              
147             sub usage {
148 0     0   0 warn <
149             Usage: perl -MDBI::Shell -e shell [ [ []]]
150             USAGE
151             }
152              
153             sub log {
154 168     168   321 my $sh = shift;
155 168 50       4996 return ($sh->{batch}) ? warn @_,"\n" : $sh->print_buffer_nop(@_,"\n"); # XXX maybe
156             }
157              
158             sub alert { # XXX not quite sure how alert and err relate
159             # for msgs that would pop-up an alert dialog if this was a Tk app
160 0     0   0 my $sh = shift;
161 0         0 return warn @_,"\n";
162             }
163              
164             sub err { # XXX not quite sure how alert and err relate
165 0     0   0 my ($sh, $msg, $die) = @_;
166 0         0 $msg = "DBI::Shell: $msg\n";
167 0 0       0 die $msg if $die;
168 0         0 return $sh->alert($msg);
169             }
170              
171              
172              
173             sub add_option {
174 222     222   364 my ($sh, $opt, $default) = @_;
175 222         640 (my $opt_name = $opt) =~ s/[|=].*//;
176             croak "Can't add_option '$opt_name', already defined"
177 222 100       4509 if exists $sh->{$opt_name};
178 202         381 $sh->{options}->{$opt_name} = $opt;
179 202         404 $sh->{$opt_name} = $default;
180             }
181              
182             sub load_plugins {
183 22     22   63 my ($sh, @ppi) = @_;
184             # Output must not appear while loading plugins:
185             # It might happen, that batch mode is entered
186             # later!
187 22         35 my @pi;
188 22 50       57 return unless(@ppi);
189 22         69 foreach my $n (0 .. $#ppi) {
190 22 100       66 next unless ($ppi[$n]);
191 12         26 my $pi = $ppi[$n];
192              
193 12 100       56 if ( ref $pi eq 'HASH' ) {
    50          
194             # As we descend down the hash reference,
195             # we're looking for an array of modules to source in.
196 4         18 my @mpi = keys %$pi;
197 4         15 foreach my $opt (@mpi) {
198             #print "Working with $opt\n";
199 12 100       98 if ($opt =~ /^option/i) {
    100          
    50          
200             # Call the option handling.
201 4         11 $sh->install_options( @{$pi->{$opt}} );
  4         53  
202 4         13 next;
203             } elsif ( $opt =~ /^database/i ) {
204             # Handle plugs for a named # type of database.
205 4 50       19 next unless $sh->{dbh};
206             # Determine what type of database connection.
207 4         56 my $db = $sh->{dbh}->{Driver}->{Name};
208             $sh->load_plugins( $pi->{$opt}->{$db} )
209 4 50       108 if (exists $pi->{$opt}->{$db});
210 4         11 next;
211             } elsif ( $opt =~ /^non-database/i ) {
212 4         29 $sh->load_plugins( $pi->{$opt} );
213             } else {
214 0         0 $sh->load_plugins( $pi->{$opt} );
215             }
216             }
217             } elsif ( ref $pi eq 'ARRAY' ) {
218 8         24 @pi = @$pi;
219             } else {
220 0 0       0 next unless $pi;
221 0         0 push(@pi, $pi);
222             }
223 12         31 foreach my $pi (@pi) {
224 16         33 my $mod = $pi;
225 16         42 $mod =~ s/\.pm$//;
226             #print "Module: $mod\n";
227 16         535 unshift @DBI::Shell::Std::ISA, $mod;
228 16     4   1310 eval qq{ use $pi };
  4     4   2332  
  4     4   14  
  4     4   93  
  4         2401  
  4         13  
  4         80  
  4         2156  
  4         14  
  4         85  
  4         2745  
  4         15  
  4         102  
229 16 50       74 if ($@) {
230 0         0 warn "Failed: $@";
231 0         0 shift @DBI::Shell::Std::ISA;
232 0         0 shift @pi;
233             } else {
234             $sh->print_buffer_nop("Loaded plugins $mod\n")
235 16 50       96 unless $sh->{batch};
236             }
237             }
238             }
239 22         129 local ($|) = 1;
240             # plug-ins should remove options they recognise from (localized) @ARGV
241             # by calling Getopt::Long::GetOptions (which is already in pass_through mode).
242 22         54 foreach my $pi (@pi) {
243 16         53 local *ARGV = $sh->{unhandled_options};
244 16         80 $pi->init($sh);
245             }
246 22         93 return @pi;
247             }
248              
249             sub default_config {
250 7     7   16 my $sh = shift;
251             #
252             # Set default configuration options
253             #
254 7   50     277 foreach my $opt_ref (
      50        
      33        
255             [ 'command_prefix_line=s' => '/' ],
256             [ 'command_prefix_end=s' => ';' ],
257             [ 'command_prefix=s' => '[/;]' ],
258             [ 'chistory_size=i' => 50 ],
259             [ 'rhistory_size=i' => 50 ],
260             [ 'rhistory_head=i' => 5 ],
261             [ 'rhistory_tail=i' => 5 ],
262             [ 'user_level=i' => 1 ],
263             [ 'editor|ed=s' => ($ENV{VISUAL} || $ENV{EDITOR} || 'vi') ],
264             [ 'batch' => 0 ],
265             [ 'format=s' => 'neat' ],
266             [ 'prompt=s' => undef ],
267             # defaults for each new database connect:
268             [ 'init_trace|trace=i' => 0 ],
269             [ 'init_autocommit|autocommit=i' => 1 ],
270             [ 'debug|d=i' => ($ENV{DBISH_DEBUG} || 0) ],
271             [ 'seperator|sep=s' => ',' ],
272             [ 'sqlpath|sql=s' => '.' ],
273             [ 'tmp_dir|tmp_d=s' => $ENV{DBISH_TMP} ],
274             [ 'tmp_file|tmp_f=s' => qq{dbish$$.sql} ],
275             [ 'home_dir|home_d=s' => $ENV{HOME} || "$ENV{HOMEDRIVE}$ENV{HOMEPATH}" ],
276             [ 'desc_show_remarks|show_remarks' => 1 ],
277             [ 'desc_show_long|show_long' => 1 ],
278             [ 'desc_format=s' => q{partbox} ],
279             [ 'desc_show_columns=s' => q{COLUMN_NAME,DATA_TYPE,TYPE_NAME,COLUMN_SIZE,PK,NULLABLE,COLUMN_DEF,IS_NULLABLE,REMARKS} ],
280             [ 'null_format=s' => '(NULL)' ],
281             [ 'bool_format=s' => q{Y,N} ],
282             @_,
283             ) {
284 182         338 $sh->add_option(@$opt_ref);
285             }
286              
287             }
288            
289              
290             sub default_commands {
291 7     7   30 my $sh = shift;
292             #
293             # Install default commands
294             #
295             # The sub is passed a reference to the shell and the @ARGV-style
296             # args it was invoked with.
297             #
298             $sh->{commands} = {
299 7         270 'help' => {
300             hint => "display this list of commands",
301             },
302             'quit' => {
303             hint => "exit",
304             },
305             'exit' => {
306             hint => "exit",
307             },
308             'trace' => {
309             hint => "set DBI trace level for current database",
310             },
311             'connect' => {
312             hint => "connect to another data source/DSN",
313             },
314             'prompt' => {
315             hint => "change the displayed prompt",
316             },
317             # --- execute commands
318             'go' => {
319             hint => "execute the current statement",
320             },
321             'count' => {
322             hint => "execute 'select count(*) from table' (on each table listed).",
323             },
324             'do' => {
325             hint => "execute the current (non-select) statement",
326             },
327             'perl' => {
328             hint => "evaluate the current statement as perl code",
329             },
330             'ping' => {
331             hint => "ping the current connection",
332             },
333             'commit' => {
334             hint => "commit changes to the database",
335             },
336             'rollback' => {
337             hint => "rollback changes to the database",
338             },
339             # --- information commands
340             'primary_key_info' => {
341             hint => "display primary keys that exist in current database",
342             },
343             'col_info' => {
344             hint => "display columns that exist in current database",
345             },
346             'table_info' => {
347             hint => "display tables that exist in current database",
348             },
349             'type_info' => {
350             hint => "display data types supported by current server",
351             },
352             'drivers' => {
353             hint => "display available DBI drivers",
354             },
355              
356             # --- statement/history management commands
357             'clear' => {
358             hint => "erase the current statement",
359             },
360             'redo' => {
361             hint => "re-execute the previously executed statement",
362             },
363             'get' => {
364             hint => "make a previous statement current again",
365             },
366             'current' => {
367             hint => "display current statement",
368             },
369             'edit' => {
370             hint => "edit current statement in an external editor",
371             },
372             'chistory' => {
373             hint => "display command history",
374             },
375             'rhistory' => {
376             hint => "display result history",
377             },
378             'format' => {
379             hint => "set display format for selected data (Neat|Box)",
380             },
381             'history' => {
382             hint => "display combined command and result history",
383             },
384             'option' => {
385             hint => "display or set an option value",
386             },
387             'describe' => {
388             hint => "display information about a table (columns, data types).",
389             },
390             'load' => {
391             hint => "load a file from disk to the current buffer.",
392             },
393             'run' => {
394             hint => "load a file from disk to current buffer, then executes.",
395             },
396             'save' => {
397             hint => "save the current buffer to a disk file.",
398             },
399             'spool' => {
400             hint => "send all output to a disk file. usage: spool file name or spool off.",
401             },
402              
403             };
404              
405             }
406              
407             sub default_term {
408 7     7   21 my ($sh, $class) = @_;
409             #
410             # Setup Term
411             #
412 7         13 my $mode;
413 7 50       42 if (!is_interactive()) {
414 7         151 $sh->{batch} = 1;
415 7         16 $mode = "in batch mode";
416             } else {
417 0         0 $sh->{term} = new Term::ReadLine($class);
418 0 0       0 if ($sh->{term}->Features->{readHistory}) {
419 0         0 $sh->{term}->ReadHistory(File::Spec->catfile(File::HomeDir->my_home, HISTORY_FILE));
420             }
421 0         0 $mode = "";
422             }
423              
424 7         20 return( $mode );
425             }
426              
427             sub new {
428 7     7   148 my ($class, @args) = @_;
429              
430 7         23 my $sh = bless {}, $class;
431            
432 7         47 $sh->default_config;
433 7         72 $sh->default_commands;
434              
435             #
436             # Handle command line parameters
437             #
438             # data_source and user command line parameters overrides both
439             # environment and config settings.
440             #
441              
442 7         29 $DB::single = 1;
443              
444 7         28 local (@ARGV) = @args;
445 7         14 my @options = values %{ $sh->{options} };
  7         53  
446 7         44 Getopt::Long::config('pass_through'); # for plug-ins
447 7 50       673 unless (GetOptions($sh, 'help|h', @options)) {
448 0         0 $class->usage;
449 0         0 croak "DBI::Shell aborted.\n";
450             }
451 7 50       13226 if ($sh->{help}) {
452 0         0 $class->usage;
453 0         0 return;
454             }
455              
456 7         25 $sh->{unhandled_options} = [];
457 7         29 @args = ();
458 7         21 foreach my $arg (@ARGV) {
459 7 50       26 if ($arg =~ /^-/) { # expected to be in "--opt=value" format
460 0         0 push @{$sh->{unhandled_options}}, $arg;
  0         0  
461             }
462             else {
463 7         22 push @args, $arg;
464             }
465             }
466              
467             # This may be obsolete since it is run again in DBI::Shell::new.
468 7         61 $sh->do_format($sh->{format});
469              
470 7   0     33 $sh->{data_source} = shift(@args) || $ENV{DBI_DSN} || '';
471              
472 7         16 my $user = shift(@args);
473 7 50 50     60 $sh->{user} = defined $user ? $user : $ENV{DBI_USER} || '';
474 7         16 my $password = shift(@args);
475 7 50 50     44 $sh->{password} = defined $password ? $password : $ENV{DBI_PASS} || undef;
476              
477 7         16 $sh->{chistory} = []; # command history
478 7         18 $sh->{rhistory} = []; # result history
479 7         14 $sh->{prompt} = $sh->{data_source};
480              
481             # set the default io handle.
482 7         18 $sh->{out_fh} = \*STDOUT;
483              
484             # support for spool command ...
485 7         15 $sh->{spooling} = 0; $sh->{spool_file} = undef; $sh->{spool_fh} = undef;
  7         14  
  7         24  
486              
487 7         51 my $mode = $sh->default_term($class);
488              
489 7         129 $sh->log("DBI::Shell $DBI::Shell::VERSION using DBI $DBI::VERSION $mode");
490 7 50       50 $sh->log("DBI::Shell loaded from $INC{'DBI/Shell.pm'}") if $sh->{debug};
491              
492 7         38 return $sh;
493             }
494              
495             # Used to install, configure, or change an option or command.
496             sub install_options {
497 52     52   110 my ($sh, $options) = @_;
498              
499 52         72 my @po;
500             $sh->log( "reference type: " . ref $options )
501 52 50       117 if $sh->{debug};
502              
503 52 50       117 if ( ref $options eq 'ARRAY' ) {
    0          
    0          
504              
505 52         110 foreach my $opt_ref ( @$options )
506             #[ 'debug|d=i' => ($ENV{DBISH_DEBUG} || 0) ],
507             #[ 'seperator|sep=s' => ',' ],)
508             {
509 116 100       209 if ( ref $opt_ref eq 'ARRAY' ) {
510 36         86 $sh->install_options( $opt_ref );
511             } else {
512 80         151 push( @po, $opt_ref );
513             }
514             }
515             } elsif ( ref $options eq 'HASH' ) {
516 0         0 foreach (keys %{$options}) {
  0         0  
517 0         0 push(@po, $_, $options->{$_});
518             }
519             } elsif ( ref $options eq 'SCALAR' ) {
520 0         0 push( @po, $$options );
521             } else {
522 0 0       0 return unless $options;
523 0         0 push( @po, $options );
524             }
525              
526 52 100       115 return unless @po;
527              
528 40         72 eval{ $sh->add_option(@po) };
  40         99  
529             # Option exists, just change it.
530 40 100       366 if ($@ =~ /add_option/) {
531 20         126 $sh->do_option( join( '=',@po ) );
532             } else {
533 20 50       58 croak "configuration: $@\n" if $@;
534             }
535             }
536              
537             sub configuration {
538 5     5   18 my $sh = shift;
539              
540             # Source config file which may override the defaults.
541             # Default is $ENV{HOME}/.dbish_config.
542             # Can be overridden with $ENV{DBISH_CONFIG}.
543             # Make $ENV{DBISH_CONFIG} empty to prevent sourcing config file.
544             # XXX all this will change
545             my $homedir = $ENV{HOME} # unix
546 5   33     38 || "$ENV{HOMEDRIVE}$ENV{HOMEPATH}"; # NT
547 5   66     42 $sh->{config_file} = $ENV{DBISH_CONFIG} || "$homedir/.dbish_config";
548 5         18 my $config;
549 5 100 66     150 if ($sh->{config_file} && -f $sh->{config_file}) {
550 4         224 my $full = File::Spec->rel2abs( $sh->{config_file} );
551 4         2380 $config = require $full;
552             # allow for custom configuration options.
553 4 50       27 if (exists $config->{'options'} ) {
554 4         52 $sh->install_options( $config->{'options'} );
555             }
556             }
557 5         20 return $config;
558             }
559              
560              
561             sub run {
562 0     0   0 my $sh = shift;
563              
564 0         0 my $current_line = '';
565              
566 0         0 while (1) {
567 0         0 my $prefix = $sh->{command_prefix};
568              
569 0         0 $current_line = $sh->readline($sh->prompt());
570 0 0       0 $current_line = "/quit" unless defined $current_line;
571              
572 0         0 my $copy_cline = $current_line; my $eat_line = 0;
  0         0  
573             # move past command prefix contained within quotes
574 0         0 while( $copy_cline =~ s/(['"][^'"]*(?:$prefix).*?['"])//og ) {
575 0         0 $eat_line = $+[0];
576             }
577              
578             # What's left to check?
579 0         0 my $line;
580 0 0       0 if ($eat_line > 0) {
581 0         0 $sh->{current_buffer} .= substr( $current_line, 0, $eat_line ) . "\n";
582 0 0       0 $current_line = substr( $current_line, $eat_line )
583             if (length($current_line) >= $eat_line );
584             } else {
585 0         0 $current_line = $copy_cline;
586             }
587              
588              
589 0 0       0 if (
    0          
590             $current_line =~ m/
591             ^(.*?)
592             (?
593             $prefix
594             (?:(\w*)
595             ([^\|><]*))?
596             ((?:\||>>?|<
597             $
598             /x) {
599 0   0     0 my ($stmt, $cmd, $args_string, $output) = ($1, $2, $3, $4||'');
600              
601             # print "$stmt -- $cmd -- $args_string -- $output\n";
602             # $sh->{current_buffer} .= "$stmt\n" if length $stmt;
603 0 0       0 if (length $stmt) {
604 0         0 $stmt =~ s/\\$prefix/$prefix/g;
605 0         0 $sh->{current_buffer} .= "$stmt\n";
606 0 0       0 if ($sh->is_spooling) { print ${$sh->{spool_fh}} ($stmt, "\n\n") }
  0         0  
  0         0  
607             }
608              
609 0 0       0 $cmd = 'go' if $cmd eq '';
610 0   0     0 my @args = split ' ', $args_string||'';
611              
612             warn("command='$cmd' args='$args_string' output='$output'")
613 0 0       0 if $sh->{debug};
614              
615 0         0 my $command;
616 0 0       0 if ($sh->{abbrev}) {
617 0         0 $command = $sh->{abbrev}->{$cmd};
618             }
619             else {
620 0 0       0 $command = ($sh->{command}->{$cmd}) ? $cmd : undef;
621             }
622 0 0       0 if ($command) {
623 0         0 $sh->run_command($command, $output, @args);
624             }
625             else {
626 0 0       0 if ($sh->{batch}) {
627 0         0 die "Command '$cmd' not recognised";
628             }
629 0         0 $sh->alert("Command '$cmd' not recognised ",
630             "(enter ${prefix}help for help).");
631             }
632              
633             }
634             elsif ($current_line ne "") {
635 0 0       0 if ($sh->is_spooling) { print ${$sh->{spool_fh}} ($current_line, "\n") }
  0         0  
  0         0  
636 0         0 $sh->{current_buffer} .= $current_line . "\n";
637             # print whole buffer here so user can see it as
638             # it grows (and new users might guess that unrecognised
639             # inputs are treated as commands)
640 0 0       0 unless ($sh->{user_level}) {
641 0         0 $sh->run_command('current', undef,
642             "(enter '$prefix' to execute or '${prefix}help' for help)");
643             }
644             }
645             }
646             }
647            
648              
649             #
650             # Internal methods
651             #
652              
653             sub readline {
654 0     0   0 my ($sh, $prompt) = @_;
655 0         0 my $rv;
656 0 0       0 if ($sh->{term}) {
657 0         0 $rv = $sh->{term}->readline($prompt);
658             }
659             else {
660 0         0 chomp($rv = );
661             }
662              
663 0         0 return $rv;
664             }
665              
666              
667             sub run_command {
668 9     9   41 my ($sh, $command, $output, @args) = @_;
669 9 50       31 return unless $command;
670              
671 9         31 my $code = "do_$command";
672 9 50       89 if ($sh->can("$code")) {
673 9 50       28 local(*STDOUT) if $output;
674 9 50       24 local(*OUTPUT) if $output;
675 9 50       27 if ($output) {
676 0 0       0 if (open(OUTPUT, $output)) {
677 0         0 *STDOUT = *OUTPUT;
678             } else {
679 0         0 $sh->err("Couldn't open output '$output': $!");
680 0         0 $sh->run_command('current', undef, '');
681             }
682             }
683              
684 9         17 local $@;
685              
686 9         21 eval {
687 9         37 $sh->$code(@args);
688             };
689 9 50       60 close OUTPUT if $output;
690 9 50       30 $sh->err("$command failed: $@") if $@;
691             }
692             else {
693 0 0       0 if ($command eq 'spool') {
694 0         0 $sh->err("The DBI::Shell:Spool plug in needs to be installed. See https://rt.cpan.org/Ticket/Display.html?id=24538#txn-813176")
695             }
696             else {
697 0         0 $sh->err("$command does not exist, does a plug-in need to be installed?")
698             }
699             }
700 9         84 return;
701             }
702              
703              
704             sub print_list {
705 1     1   3 my ($sh, $list_ref) = @_;
706 1         5 for(my $i = 0; $i < @$list_ref; $i++) {
707 0         0 print ${$sh->{out_fh}} ($i+1,": $$list_ref[$i]\n");
  0         0  
708             }
709 1         17 return;
710             }
711              
712              
713             #-------------------------------------------------------------------
714             #
715             # Print Buffer adding a prompt.
716             #
717             #-------------------------------------------------------------------
718             sub print_buffer {
719 72     72   120 my $sh = shift;
720             {
721 72         162 local ($,) = q{ };
  72         161  
722 72         146 my @out = @_;
723 72         163 chomp $out[-1]; # Remove any added newline.
724 72         208 return print ($sh->prompt(), @out,"\n");
725             }
726             }
727              
728             #-------------------------------------------------------------------
729             #
730             # Print Buffer without adding a prompt.
731             #
732             #-------------------------------------------------------------------
733             sub print_buffer_nop {
734 98     98   161 my $sh = shift;
735             {
736 98         121 local ($,) = q{ };
  98         228  
737 98         163 my @out = @_;
738 98         178 chomp $out[-1]; # Remove any added newline.
739 98         991 return print (@out,"\n");
740             }
741             }
742              
743             sub get_data_source {
744 9     9   29 my ($sh, $dsn, @args) = @_;
745 9         15 my $driver;
746              
747 9 50       26 if ($dsn) {
748 9 50       74 if ($dsn =~ m/^dbi:.*:/i) { # has second colon
    0          
749 9         31 return $dsn; # assumed to be full DSN
750             }
751             elsif ($dsn =~ m/^dbi:([^:]*)/i) {
752 0         0 $driver = $1 # use DriverName part
753             }
754             else {
755 0         0 $sh->print_buffer_nop ("Ignored unrecognised DBI DSN '$dsn'.\n");
756             }
757             }
758              
759 0 0       0 if ($sh->{batch}) {
760 0         0 die "Missing or unrecognised DBI DSN.";
761             }
762              
763 0         0 $sh->print_buffer_nop("\n");
764              
765 0         0 while (!$driver) {
766 0         0 $sh->print_buffer_nop("Available DBI drivers:\n");
767 0         0 my @drivers = DBI->available_drivers;
768 0         0 for( my $cnt = 0; $cnt <= $#drivers; $cnt++ ) {
769 0         0 $sh->print_buffer_nop(sprintf ("%2d: dbi:%s\n", $cnt+1, $drivers[$cnt]));
770             }
771 0         0 $driver = $sh->readline(
772             "Enter driver name or number, or full 'dbi:...:...' DSN: ");
773 0 0       0 exit unless defined $driver; # detect ^D / EOF
774 0         0 $sh->print_buffer_nop("\n");
775              
776 0 0       0 return $driver if $driver =~ /^dbi:.*:/i; # second colon entered
777              
778 0 0       0 if ( $driver =~ /^\s*(\d+)/ ) {
779 0         0 $driver = $drivers[$1-1];
780             } else {
781 0         0 $driver = $1;
782 0 0       0 $driver =~ s/^dbi://i if $driver # incase they entered 'dbi:Name'
783             }
784             # XXX try to install $driver (if true)
785             # unset $driver if install fails.
786             }
787              
788 0         0 my $source;
789 0         0 while (!defined $source) {
790 0         0 my $prompt;
791 0         0 my @data_sources = DBI->data_sources($driver);
792 0 0       0 if (@data_sources) {
793 0         0 $sh->print_buffer_nop("Enter data source to connect to: \n");
794 0         0 for( my $cnt = 0; $cnt <= $#data_sources; $cnt++ ) {
795 0         0 $sh->print_buffer_nop(sprintf ("%2d: %s\n", $cnt+1, $data_sources[$cnt]));
796             }
797 0         0 $prompt = "Enter data source or number,";
798             }
799             else {
800 0         0 $sh->print_buffer_nop ("(The data_sources method returned nothing.)\n");
801 0         0 $prompt = "Enter data source";
802             }
803 0         0 $source = $sh->readline(
804             "$prompt or full 'dbi:...:...' DSN: ");
805 0 0       0 return if !defined $source; # detect ^D / EOF
806 0 0       0 if ($source =~ /^\s*(\d+)/) {
    0          
807 0         0 $source = $data_sources[$1-1]
808             }
809             elsif ($source =~ /^dbi:([^:]+)$/) { # no second colon
810 0         0 $driver = $1; # possibly new driver
811 0         0 $source = undef;
812             }
813 0         0 $sh->print_buffer_nop("\n");
814             }
815              
816 0         0 return $source;
817             }
818              
819              
820             sub prompt_for_password {
821 0     0   0 my ($sh) = @_;
822              
823             # no prompts in batch mode.
824              
825 0 0       0 return if ($sh->{batch});
826              
827 0 0       0 if (!defined($haveTermReadKey)) {
828 0 0       0 $haveTermReadKey = eval { require Term::ReadKey } ? 1 : 0;
  0         0  
829             }
830 0         0 local $| = 1;
831 0 0       0 $sh->print_buffer_nop ("Password for $sh->{user} (",
832             ($haveTermReadKey ? "not " : "Warning: "),
833             "echoed to screen): ");
834 0 0       0 if ($haveTermReadKey) {
835 0         0 Term::ReadKey::ReadMode('noecho');
836 0         0 $sh->{password} = Term::ReadKey::ReadLine(0);
837 0         0 Term::ReadKey::ReadMode('restore');
838             } else {
839 0         0 $sh->{password} = ;
840             }
841 0         0 chomp $sh->{password};
842 0         0 $sh->print_buffer_nop ("\n");
843             }
844              
845             sub prompt {
846 72     72   131 my ($sh) = @_;
847 72 50       1054 return "" if $sh->{batch};
848 0 0       0 return "(not connected)> " unless $sh->{dbh};
849              
850 0 0       0 if ( ref $sh->{prompt} ) {
851 0         0 foreach (@{$sh->{prompt}} ) {
  0         0  
852 0 0       0 if ( ref $_ eq "CODE" ) {
853 0         0 $sh->{prompt} .= &$_;
854             } else {
855 0         0 $sh->{prompt} .= $_;
856             }
857             }
858 0         0 return "$sh->{user}\@$sh->{prompt}> ";
859             } else {
860 0         0 return "$sh->{user}\@$sh->{prompt}> ";
861             }
862 0         0 return;
863             }
864              
865              
866             sub push_chistory {
867 52     52   144 my ($sh, $cmd) = @_;
868 52 50       159 $cmd = $sh->{current_buffer} unless defined $cmd;
869 52         92 $sh->{prev_buffer} = $cmd;
870 52         123 my $chist = $sh->{chistory};
871 52 50       170 shift @$chist if @$chist >= $sh->{chistory_size};
872 52         153 push @$chist, $cmd;
873 52         115 return;
874             }
875              
876              
877             #
878             # Command methods
879             #
880              
881             sub do_help {
882 1     1   4 my ($sh, @args) = @_;
883              
884 1 50       9 return "" if $sh->{batch};
885              
886 0         0 my $prefix = $sh->{command_prefix};
887 0         0 my $commands = $sh->{commands};
888 0         0 $sh->print_buffer_nop ("Defined commands, in alphabetical order:\n");
889 0         0 foreach my $cmd (sort keys %$commands) {
890 0   0     0 my $hint = $commands->{$cmd}->{hint} || '';
891 0         0 $sh->print_buffer_nop(sprintf (" %s%-10s %s\n", $prefix, $cmd, $hint));
892             }
893 0 0       0 $sh->print_buffer_nop ("Commands can be abbreviated.\n") if $sh->{abbrev};
894 0         0 return;
895             }
896              
897              
898             sub do_format {
899 22     22   1970 my ($sh, @args) = @_;
900 22   50     81 my $mode = $args[0] || '';
901 22         41 my $class = eval { DBI::Format->formatter($mode,1) };
  22         176  
902 22 50       69 unless ($class) {
903 0         0 return $sh->alert("Unable to select '$mode': $@");
904             }
905 22 50       74 $sh->log("Using formatter class '$class'") if $sh->{debug};
906 22         56 $sh->{format} = $mode;
907 22         108 return $sh->{display} = $class->new($sh);
908             }
909              
910              
911             sub do_go {
912 52     52   160 my ($sh, @args) = @_;
913              
914             # print "do_go\n";
915              
916             # Modify go to get the last executed statement if called on an
917             # empty buffer.
918              
919 52 100       257 if ($sh->{current_buffer} eq '') {
920 2         21 $sh->do_get;
921 2 50       8 return if $sh->{current_buffer} eq '';
922             }
923              
924 52         139 $sh->{prev_buffer} = $sh->{current_buffer};
925              
926 52         235 $sh->push_chistory;
927            
928 52         141 eval {
929             # Determine if the single quotes are out of balance.
930 52         173 my $count = ($sh->{current_buffer} =~ tr/'/'/);
931 52 50       194 warn "Quotes out of balance: $count" unless (($count % 2) == 0);
932              
933 52         541 my $sth = $sh->{dbh}->prepare($sh->{current_buffer});
934              
935 51         7349 $sh->sth_go($sth, 1);
936             };
937 52 100       254 if ($@) {
938 1         3 my $err = $@;
939             $err =~ s: at \S*DBI/Shell.pm line \d+(,.*?chunk \d+)?::
940 1 50 33     18 if !$sh->{debug} && $err =~ /^DBD::\w+::\w+ \w+/;
941 1         42 print STDERR "$err";
942             }
943             # There need to be a better way, maybe clearing the
944             # buffer when the next non command is typed.
945             # Or sprinkle <$sh->{current_buffer} ||= $sh->{prev_buffer};>
946             # around in the code.
947 52         1172 return $sh->{current_buffer} = '';
948             }
949              
950              
951             sub sth_go {
952 53     53   165 my ($sh, $sth, $execute, $rh) = @_;
953              
954 53 100       161 $rh = 1 unless defined $rh; # Add to results history. Default 1, Yes.
955 53         80 my $rv;
956 53 50 66     182 if ($execute || !$sth->{Active}) {
957 53         110 my @params;
958 53   50     535 my $params = $sth->{NUM_OF_PARAMS} || 0;
959 53 50       219 $sh->print_buffer_nop("Statement has $params parameters:\n") if $params;
960 53         168 foreach(1..$params) {
961 0         0 my $val = $sh->readline("Parameter $_ value: ");
962 0         0 push @params, $val;
963             }
964 53         295 $rv = $sth->execute(@params);
965             }
966            
967 53 50       6352 if (!$sth->{'NUM_OF_FIELDS'}) { # not a select statement
968 0         0 local $^W=0;
969 0 0       0 $rv = "undefined number of" unless defined $rv;
970 0 0       0 $rv = "unknown number of" if $rv == -1;
971 0 0       0 $sh->print_buffer_nop ("[$rv row" . ($rv==1 ? "" : "s") . " affected]\n");
972 0         0 return;
973             }
974              
975 53         770 $sh->{sth} = $sth;
976              
977             #
978             # Remove oldest result from history if reached limit
979             #
980 53         125 my $rhist = $sh->{rhistory};
981 53 100       129 if ($rh) {
982 51 50       167 shift @$rhist if @$rhist >= $sh->{rhistory_size};
983 51         149 push @$rhist, [];
984             }
985              
986             #
987             # Keep a buffer of $sh->{rhistory_tail} many rows,
988             # when done with result add those to rhistory buffer.
989             # Could use $sth->rows(), but not all DBD's support it.
990             #
991 53         104 my @rtail;
992 53         83 my $i = 0;
993 53   50     188 my $display = $sh->{display} || die "panic: no display set";
994 53   50     388 $display->header($sth, $sh->{out_fh}||\*STDOUT, $sh->{seperator});
995              
996             OUT_ROWS:
997 53         501 while (my $rowref = $sth->fetchrow_arrayref()) {
998 519         33435 $i++;
999              
1000 519         1585 my $rslt = $display->row($rowref);
1001              
1002 519 100       13876 if($rh) {
1003 510 100       1502 if ($i <= $sh->{rhistory_head}) {
1004 255         351 push @{$rhist->[-1]}, [@$rowref];
  255         815  
1005             }
1006             else {
1007 255 50       655 shift @rtail if @rtail == $sh->{rhistory_tail};
1008 255         704 push @rtail, [@$rowref];
1009             }
1010             }
1011              
1012 519 50       3226 unless(defined $rslt) {
1013 0         0 $sh->print_buffer_nop( "row limit reached" );
1014 0         0 last OUT_ROWS;
1015             }
1016             }
1017              
1018 53         3034 $display->trailer($i);
1019              
1020 53 100       162 if($rh) {
1021 51 50       132 if (@rtail) {
1022 51         88 my $rows = $i;
1023 51         131 my $ommitted = $i - $sh->{rhistory_head} - @rtail;
1024             # Only include the omitted message if results are omitted.
1025 51 50       130 if ($ommitted) {
1026 0         0 push(@{$rhist->[-1]},
  0         0  
1027             [ "[...$ommitted rows out of $rows ommitted...]"]);
1028             }
1029 51         116 foreach my $rowref (@rtail) {
1030 255         332 push @{$rhist->[-1]}, $rowref;
  255         552  
1031             }
1032             }
1033             }
1034              
1035 53         166 return;
1036             }
1037              
1038             #------------------------------------------------------------------
1039             #
1040             # Generate a select count(*) from table for each table in list.
1041             #
1042             #------------------------------------------------------------------
1043              
1044             sub do_count {
1045 0     0   0 my ($sh, @args) = @_;
1046 0         0 foreach my $tab (@args) {
1047 0         0 $sh->print_buffer_nop ("Counting: $tab\n");
1048 0         0 $sh->{current_buffer} = "select count(*) as cnt_$tab from $tab";
1049 0         0 $sh->do_go();
1050             }
1051 0         0 return $sh->{current_buffer} = '';
1052             }
1053              
1054             sub do_do {
1055 0     0   0 my ($sh, @args) = @_;
1056 0         0 $sh->push_chistory;
1057 0         0 my $rv = $sh->{dbh}->do($sh->{current_buffer});
1058 0 0       0 $sh->print_buffer_nop ("[$rv row" . ($rv==1 ? "" : "s") . " affected]\n")
    0          
1059             if defined $rv;
1060              
1061             # XXX I question setting the buffer to '' here.
1062             # I may want to edit my line without having to scroll back.
1063 0         0 return $sh->{current_buffer} = '';
1064             }
1065              
1066              
1067             sub do_disconnect {
1068 9     9   30 my ($sh, @args) = @_;
1069 9 50       53 return unless $sh->{dbh};
1070 9         50 $sh->log("Disconnecting from $sh->{data_source}.");
1071 9         37 eval {
1072 9 100       80 $sh->{sth}->finish if $sh->{sth};
1073 9 50       149 $sh->{dbh}->rollback unless $sh->{dbh}->{AutoCommit};
1074 9         171 $sh->{dbh}->disconnect;
1075             };
1076 9 50       177 $sh->alert("Error during disconnect: $@") if $@;
1077 9         73 $sh->{sth} = undef;
1078 9         56 $sh->{dbh} = undef;
1079 9         245 return;
1080             }
1081              
1082              
1083             sub do_connect {
1084 9     9   29 my ($sh, $dsn, $user, $pass) = @_;
1085              
1086 9         62 $dsn = $sh->get_data_source($dsn);
1087 9 50       38 return unless $dsn;
1088              
1089 9 100       50 $sh->do_disconnect if $sh->{dbh};
1090              
1091             # Change from Jeff Zucker, convert literal slash and letter n to newline.
1092 9         27 $dsn =~ s/\\n/\n/g;
1093 9         20 $dsn =~ s/\\t/\t/g;
1094              
1095              
1096 9         23 $sh->{data_source} = $dsn;
1097 9 50 33     44 if (defined $user and length $user) {
1098 0         0 $sh->{user} = $user;
1099 0         0 $sh->{password} = undef; # force prompt below
1100             }
1101              
1102 9         65 $sh->log("Connecting to '$sh->{data_source}' as '$sh->{user}'...");
1103 9 50 33     56 if ($sh->{user} and !defined $sh->{password}) {
1104 0         0 $sh->prompt_for_password();
1105             }
1106             $sh->{dbh} = DBI->connect(
1107             $sh->{data_source}, $sh->{user}, $sh->{password}, {
1108             AutoCommit => $sh->{init_autocommit},
1109 9         114 PrintError => 0,
1110             RaiseError => 1,
1111             LongTruncOk => 1, # XXX
1112             });
1113 9 50       29960 $sh->{dbh}->trace($sh->{init_trace}) if $sh->{init_trace};
1114 9         24 return;
1115             }
1116              
1117              
1118             sub do_current {
1119 9     9   29 my ($sh, $msg, @args) = @_;
1120 9 50       30 $msg = $msg ? " $msg" : "";
1121             return
1122 9         65 $sh->log("Current statement buffer$msg:\n" . $sh->{current_buffer});
1123             }
1124              
1125             sub do_autoflush {
1126              
1127 0     0   0 return;
1128             }
1129              
1130             sub do_trace {
1131 0     0   0 return shift->{dbh}->trace(@_);
1132             }
1133              
1134             sub do_commit {
1135 1     1   17 return shift->{dbh}->commit(@_);
1136             }
1137              
1138             sub do_rollback {
1139 1     1   14 return shift->{dbh}->rollback(@_);
1140             }
1141              
1142              
1143             sub do_quit {
1144 0     0   0 my ($sh, @args) = @_;
1145 0 0       0 $sh->do_disconnect if $sh->{dbh};
1146              
1147 0 0       0 if ($sh->{term}) {
1148 0 0       0 if ($sh->{term}->Features()->{writeHistory}) {
1149 0         0 $sh->{term}->WriteHistory(File::Spec->catfile(File::HomeDir->my_home, HISTORY_FILE));
1150             }
1151             }
1152              
1153 0         0 undef $sh->{term};
1154 0         0 exit 0;
1155             }
1156              
1157             # Until the alias command is working each command requires definition.
1158 0     0   0 sub do_exit { shift->do_quit(@_); }
1159              
1160             sub do_clear {
1161 1     1   4 my ($sh, @args) = @_;
1162 1         5 return $sh->{current_buffer} = '';
1163             }
1164              
1165              
1166             sub do_redo {
1167 0     0   0 my ($sh, @args) = @_;
1168 0   0     0 $sh->{current_buffer} = $sh->{prev_buffer} || '';
1169 0 0       0 $sh->run_command('go') if $sh->{current_buffer};
1170 0         0 return;
1171             }
1172              
1173              
1174             sub do_chistory {
1175 1     1   3 my ($sh, @args) = @_;
1176 1         6 return $sh->print_list($sh->{chistory});
1177             }
1178              
1179             sub do_history {
1180 1     1   3 my ($sh, @args) = @_;
1181 1         3 for(my $i = 0; $i < @{$sh->{chistory}}; $i++) {
  1         6  
1182 0         0 $sh->print_buffer_nop ($i+1, ":\n", $sh->{chistory}->[$i], "--------\n");
1183 0         0 foreach my $rowref (@{$sh->{rhistory}[$i]}) {
  0         0  
1184 0 0       0 $sh->print_buffer_nop(" ", join(", ", map { defined $_ ? $_ : q{undef} }@$rowref), "\n");
  0         0  
1185             }
1186             }
1187 1         4 return;
1188             }
1189              
1190             sub do_rhistory {
1191 1     1   4 my ($sh, @args) = @_;
1192 1         3 for(my $i = 0; $i < @{$sh->{rhistory}}; $i++) {
  1         5  
1193 0         0 $sh->print_buffer_nop ($i+1, ":\n");
1194 0         0 foreach my $rowref (@{$sh->{rhistory}[$i]}) {
  0         0  
1195 0 0       0 $sh->print_buffer_nop (" ", join(", ", map { defined $_ ? $_ : q{undef} }@$rowref), "\n");
  0         0  
1196             }
1197             }
1198 1         5 return;
1199             }
1200              
1201              
1202             sub do_get {
1203 26     26   372 my ($sh, $num, @args) = @_;
1204             # If get is called without a number, retrieve the last command.
1205 26 100       93 unless( $num ) {
1206 23         35 $num = ($#{$sh->{chistory}} + 1);
  23         64  
1207              
1208             }
1209             # Allow for negative history. If called with -1, get the second
1210             # to last command execute, -2 third to last, ...
1211 26 100 66     146 if ($num and $num =~ /^\-\d+$/) {
1212 2         32 $sh->print_buffer_nop("Negative number $num: \n");
1213 2         7 $num = ($#{$sh->{chistory}} + 1) + $num;
  2         7  
1214 2         9 $sh->print_buffer_nop("Changed number $num: \n");
1215             }
1216              
1217 26 50 33     314 if (!$num or $num !~ /^\d+$/ or !defined($sh->{chistory}->[$num-1])) {
      33        
1218 0         0 return $sh->err("No such command number '$num'. Use /chistory to list previous commands.");
1219             }
1220 26         75 $sh->{current_buffer} = $sh->{chistory}->[$num-1];
1221 26         106 $sh->print_buffer($sh->{current_buffer});
1222 26         172 return $num;
1223             }
1224              
1225              
1226             sub do_perl {
1227 0     0   0 my ($sh, @args) = @_;
1228 0         0 $DBI::Shell::eval::dbh = $sh->{dbh};
1229 0         0 eval "package DBI::Shell::eval; $sh->{current_buffer}";
1230 0 0       0 if ($@) { $sh->err("Perl failed: $@") }
  0         0  
1231 0         0 return $sh->run_command('clear');
1232             }
1233              
1234             #-------------------------------------------------------------
1235             # Ping the current database connection.
1236             #-------------------------------------------------------------
1237             sub do_ping {
1238 0     0   0 my ($sh, @args) = @_;
1239             return $sh->print_buffer_nop (
1240             "Connection "
1241 0 0       0 , $sh->{dbh}->ping() == '0' ? 'Is' : 'Is Not'
1242             , " alive\n" );
1243             }
1244              
1245             sub do_edit {
1246 0     0   0 my ($sh, @args) = @_;
1247              
1248 0 0 0     0 $sh->run_command('get', '', $&) if @args and $args[0] =~ /^\d+$/;
1249 0   0     0 $sh->{current_buffer} ||= $sh->{prev_buffer};
1250            
1251             # Find an area to write a temp file into.
1252             my $tmp_dir = $sh->{tmp_dir} ||
1253             $ENV{DBISH_TMP} || # Give people the choice.
1254             $ENV{TMP} || # Is TMP set?
1255             $ENV{TEMP} || # How about TEMP?
1256             $ENV{HOME} || # Look for HOME?
1257             $ENV{HOMEDRIVE} . $ENV{HOMEPATH} || # Last env checked.
1258 0   0     0 "."; # fallback: try to write in current directory.
1259              
1260 0   0     0 my $tmp_file = "$tmp_dir/" . ($sh->{tmp_file} || qq{dbish$$.sql});
1261              
1262 0 0       0 $sh->log( "using tmp file: $tmp_file" ) if $sh->{debug};
1263              
1264 0         0 local (*FH);
1265 0 0       0 open(FH, ">$tmp_file") or
1266             $sh->err("Can't create $tmp_file: $!\n", 1);
1267 0 0       0 print FH $sh->{current_buffer} if defined $sh->{current_buffer};
1268 0 0       0 close(FH) or $sh->err("Can't write $tmp_file: $!\n", 1);
1269              
1270 0         0 my $command = "$sh->{editor} $tmp_file";
1271 0         0 system($command);
1272              
1273             # Read changes back in (editor may have deleted and rewritten file)
1274 0 0       0 open(FH, "<$tmp_file") or $sh->err("Can't open $tmp_file: $!\n");
1275 0         0 $sh->{current_buffer} = join "", ;
1276 0 0       0 close(FH) or $sh->err( "Close failed: $tmp_file: $!\n" );
1277 0         0 unlink $tmp_file;
1278              
1279 0         0 return $sh->run_command('current');
1280             }
1281              
1282              
1283             #
1284             # Load a command/file from disk to the current buffer. Currently this
1285             # overwrites the current buffer with the file loaded. This may change
1286             # in the future.
1287             #
1288             sub do_load {
1289 8     8   34 my ($sh, $ufile, @args) = @_;
1290              
1291 8 50       33 unless( $ufile ) {
1292 0         0 $sh->err ( qq{load what file?} );
1293 0         0 return;
1294             }
1295              
1296             # Load file for from sqlpath.
1297 8         70 my $file = $sh->look_for_file($ufile);
1298              
1299 8 50       43 unless( $file ) {
1300 0         0 $sh->err( qq{Unable to locate file $ufile} );
1301 0         0 return;
1302             }
1303              
1304 8 50       116 unless( -f $file ) {
1305 0 0       0 $file = q{'undef'} unless $file;
1306 0         0 $sh->err( qq{Can't load $file: $!} );
1307 0         0 return;
1308             }
1309              
1310 8 50       74 $sh->log("Loading: $ufile : $file") if $sh->{debug};
1311 8         35 local (*FH);
1312 8 50       326 open(FH, "$file") or $sh->err("Can't open $file: $!");
1313 8         274 $sh->{current_buffer} = join "", ;
1314 8 50       116 close(FH) or $sh->err( "close$file failed: $!" );
1315              
1316 8         149 return $sh->run_command('current');
1317             }
1318              
1319             sub do_save {
1320 1     1   4 my ($sh, $file, @args) = @_;
1321              
1322 1 50       4 unless( $file ) {
1323 0         0 $sh->err ( qq{save to what file?} );
1324 0         0 return;
1325             }
1326              
1327 1 50       4 $sh->log("Saving... ") if $sh->{debug};
1328 1         3 local (*FH);
1329 1 50       121 open(FH, "> $file") or $sh->err("Can't open $file: $!");
1330 1         25 print FH $sh->{current_buffer};
1331 1 50       13 close(FH) or $sh->err( "close$file failed: $!" );
1332              
1333 1 50       4 $sh->log(" $file") if $sh->{debug};
1334 1         4 return $sh->run_command('current');
1335             }
1336              
1337             #
1338             # run: combines load and go.
1339             #
1340             sub do_run {
1341 0     0   0 my ($sh, $file, @args) = @_;
1342 0 0       0 return unless( ! $sh->do_load( $file ) );
1343 0 0       0 $sh->log( "running $file" ) if $sh->{debug};
1344 0 0       0 $sh->run_command('go') if $sh->{current_buffer};
1345 0         0 return;
1346             }
1347              
1348             sub do_drivers {
1349 1     1   4 my ($sh, @args) = @_;
1350 1         3 $sh->log("Available drivers:");
1351 1         17 my @drivers = DBI->available_drivers;
1352 1         424 foreach my $driver (sort @drivers) {
1353 7         37 $sh->log("\t$driver");
1354             }
1355 1         8 return;
1356             }
1357              
1358              
1359             # $sth = $dbh->column_info( $catalog, $schema, $table, $column );
1360              
1361             sub do_col_info {
1362 0     0   0 my ($sh, @args) = @_;
1363 0         0 my $dbh = $sh->{dbh};
1364              
1365 0 0       0 $sh->log( "col_info( " . join( " ", @args ) . ")" ) if $sh->{debug};
1366              
1367 0         0 my $sth = $dbh->column_info(@args);
1368 0 0       0 unless(ref $sth) {
1369 0         0 $sh->print_buffer_nop ("Driver has not implemented the column_info() method\n");
1370 0         0 $sth = undef;
1371 0         0 return;
1372             }
1373 0         0 return $sh->sth_go($sth, 0, NO_RH);
1374             }
1375              
1376              
1377             sub do_type_info {
1378 1     1   4 my ($sh, @args) = @_;
1379 1         3 my $dbh = $sh->{dbh};
1380 1         6 my $ti = $dbh->type_info_all;
1381 1         20 my $ti_cols = shift @$ti;
1382 1         7 my @names = sort { $ti_cols->{$a} <=> $ti_cols->{$b} } keys %$ti_cols;
  41         69  
1383 1         12 my $sth = $sh->prepare_from_data("type_info", $ti, \@names);
1384 1         6 return $sh->sth_go($sth, 0, NO_RH);
1385             }
1386              
1387             sub do_describe {
1388 0     0   0 my ($sh, $tab, @argv) = @_;
1389 0         0 my $dbh = $sh->{dbh};
1390              
1391             # Table to describe?
1392 0 0       0 return $sh->print_buffer_nop( "Describe what?\n" ) unless (defined $tab);
1393              
1394             # First attempt the advanced describe using column_info
1395             # $sth = $dbh->column_info( $catalog, $schema, $table, $column );
1396             #$sh->log( "col_info( " . join( " ", @args ) . ")" ) if $sh->{debug};
1397              
1398             # Need to determine which columns to include with the describe command.
1399             # TABLE_CAT,TABLE_SCHEM,TABLE_NAME,COLUMN_NAME,
1400             # DATA_TYPE,TYPE_NAME,COLUMN_SIZE,BUFFER_LENGTH,
1401             # DECIMAL_DIGITS,NUM_PREC_RADIX,NULLABLE,
1402             # REMARKS,COLUMN_DEF,SQL_DATA_TYPE,
1403             # SQL_DATETIME_SUB,CHAR_OCTET_LENGTH,ORDINAL_POSITION,
1404             # IS_NULLABLE
1405             #
1406             # desc_format: partbox
1407             # desc_show_long: 1
1408             # desc_show_remarks: 1
1409              
1410 0         0 my $schema;
1411 0 0       0 if ($tab =~ /^([^.]+)\.([^.]+)$/) {
1412 0         0 $schema = $1;
1413 0         0 $tab = $2;
1414             }
1415 0         0 my @names = ();
1416              
1417             # Determine if the short or long display type is used
1418 0 0 0     0 if (exists $sh->{desc_show_long} and $sh->{desc_show_long} == 1) {
1419              
1420 0 0 0     0 if (exists $sh->{desc_show_columns} and defined
1421             $sh->{desc_show_columns}) {
1422 0 0       0 @names = map { defined $_ ? uc $_ : () } split( /[,\s+]/, $sh->{desc_show_columns});
  0         0  
1423 0 0       0 unless (@names) { # List of columns is empty
1424 0         0 $sh->err ( qq{option desc_show_columns contains an empty list, using default} );
1425             # set the empty list to undef
1426 0         0 $sh->{desc_show_columns} = undef;
1427 0         0 @names = ();
1428 0         0 push @names, qw/COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE PK
1429             NULLABLE COLUMN_DEF IS_NULLABLE/;
1430             }
1431             } else {
1432 0         0 push @names, qw/COLUMN_NAME DATA_TYPE TYPE_NAME COLUMN_SIZE PK
1433             NULLABLE COLUMN_DEF IS_NULLABLE/;
1434             }
1435             } else {
1436 0         0 push @names, qw/COLUMN_NAME TYPE_NAME NULLABLE PK/;
1437             }
1438              
1439             # my @names = qw/COLUMN_NAME DATA_TYPE NULLABLE PK/;
1440             push @names, q{REMARKS}
1441             if (exists $sh->{desc_show_remarks}
1442             and $sh->{desc_show_remarks} == 1
1443 0 0 0     0 and (not grep { m/REMARK/i } @names));
  0   0     0  
1444              
1445 0         0 my $sth = $dbh->column_info(undef, $schema, $tab, '%');
1446              
1447 0 0       0 if (ref $sth) {
1448            
1449             # Only attempt the primary_key lookup if using the column_info call.
1450              
1451 0         0 my %pk_cols;
1452 0         0 eval {
1453 0         0 my @key_column_names = $dbh->primary_key( undef, undef, $tab );
1454             # Convert the column names to lower case for matching
1455 0         0 foreach my $idx (0 ..$#key_column_names) {
1456 0         0 $pk_cols{lc($key_column_names[$idx])} = $idx;
1457             }
1458             };
1459              
1460 0         0 my @t_data = (); # An array of arrays
1461            
1462 0         0 while (my $row = $sth->fetchrow_hashref() ) {
1463              
1464 0         0 my $col_name = $row->{COLUMN_NAME};
1465 0         0 my $col_name_lc = lc $col_name;
1466              
1467             # Use the Type name, unless undefined, they use the data type
1468             # value. TODO: Change to resolve the data_type to an ANSI data
1469             # type ... SQL_
1470 0   0     0 my $type = $row->{TYPE_NAME} || $row->{DATA_TYPE};
1471              
1472 0 0       0 if (defined $row->{COLUMN_SIZE}) {
1473 0         0 $type .= "(" . $row->{COLUMN_SIZE} . ")";
1474             }
1475 0 0       0 my $is_pk = $pk_cols{$col_name_lc} if exists $pk_cols{$col_name_lc};
1476              
1477 0         0 my @out_row;
1478 0         0 foreach my $dcol (@names) {
1479              
1480             # Add primary key
1481 0 0       0 if ($dcol eq q{PK}) {
1482 0 0       0 push @out_row, defined $is_pk ? $is_pk : q{};
1483 0         0 next;
1484             }
1485 0 0 0     0 if ($dcol eq q{TYPE_NAME} and
      0        
1486             (exists $sh->{desc_show_long} and $sh->{desc_show_long} == 0)) {
1487 0   0     0 my $type = $row->{TYPE_NAME} || $row->{DATA_TYPE};
1488 0 0       0 if (defined $row->{COLUMN_SIZE}) {
1489 0         0 $type .= "(" . $row->{COLUMN_SIZE} . ")";
1490             }
1491 0         0 push @out_row, $type;
1492 0         0 next;
1493             }
1494              
1495             # Put a blank if not defined.
1496 0 0       0 push @out_row, defined $row->{$dcol} ? $row->{$dcol} : q{};
1497              
1498             # push(my @out_row
1499             # , $col_name
1500             # , $type
1501             # , sprintf( "%4s", ($row->{NULLABLE} eq 0 ? q{N}: q{Y}))
1502             # );
1503              
1504             # push @out_row, defined $row->{REMARKS} ? $row->{REMARKS} : q{}
1505             # if (exists $sh->{desc_show_remarks}
1506             # and $sh->{desc_show_remarks} == 1);
1507             }
1508              
1509 0         0 push @t_data, \@out_row;
1510             }
1511              
1512 0         0 $sth->finish; # Complete the handler from column_info
1513              
1514              
1515             # Create a new statement handler from the data and names.
1516 0         0 $sth = $sh->prepare_from_data("describe", \@t_data, \@names);
1517              
1518             # Use the built in formatter to handle data.
1519              
1520 0 0       0 my $mode = exists $sh->{desc_format} ? $sh->{desc_format} : 'partbox';
1521 0         0 my $class = eval { DBI::Format->formatter($mode,1) };
  0         0  
1522 0 0       0 unless ($class) {
1523 0         0 return $sh->alert("Unable to select '$mode': $@");
1524             }
1525              
1526 0         0 my $display = $class->new($sh);
1527              
1528 0   0     0 $display->header($sth, $sh->{out_fh}||\*STDOUT, $sh->{seperator});
1529              
1530 0         0 my $i = 0;
1531             OUT_ROWS:
1532 0         0 while (my $rowref = $sth->fetchrow_arrayref()) {
1533 0         0 $i++;
1534 0         0 my $rslt = $display->row($rowref);
1535             }
1536              
1537 0         0 $display->trailer($i);
1538             }
1539              
1540             #
1541             # This is the old method, if the driver doesn't support the DBI column_info
1542             # meta data.
1543             #
1544 0 0       0 $tab = "$schema.$tab" if defined $schema;
1545 0         0 my $sql = qq{select * from $tab where 1 = 0};
1546 0         0 $sth = $dbh->prepare( $sql );
1547 0         0 $sth->execute;
1548 0         0 my $cnt = $#{$sth->{NAME}}; #
  0         0  
1549 0         0 @names = qw{NAME TYPE NULLABLE};
1550 0         0 my @ti;
1551 0         0 for ( my $c = 0; $c <= $cnt; $c++ ) {
1552 0   0     0 push( my @j, $sth->{NAME}->[$c] || 0 );
1553 0         0 my $m = $dbh->type_info($sth->{TYPE}->[$c]);
1554 0         0 my $s;
1555             #print "desc: $c ", $sth->{NAME}->[$c], " ",
1556             #$sth->{TYPE}->[$c], "\n";
1557 0 0       0 if (ref $m eq 'HASH') {
    0          
1558 0         0 $s = $m->{TYPE_NAME}; # . q{ } . $sth->{TYPE}->[$c];
1559             } elsif (not defined $m) {
1560             # $s = q{undef } . $sth->{TYPE}->[$c];
1561 0         0 $s = $sth->{TYPE}->[$c];
1562             } else {
1563 0         0 warn "describe: not good. Not good at all!";
1564             }
1565              
1566 0 0       0 if (defined $sth->{PRECISION}->[$c]) {
1567 0   0     0 $s .= "(" . $sth->{PRECISION}->[$c] || '';
1568             $s .= "," . $sth->{SCALE}->[$c]
1569             if ( defined $sth->{SCALE}->[$c]
1570 0 0 0     0 and $sth->{SCALE}->[$c] ne 0);
1571 0         0 $s .= ")";
1572             }
1573             push(@j, $s,
1574 0 0       0 $sth->{NULLABLE}->[$c] ne 1? qq{N}: qq{Y} );
1575 0         0 push(@ti,\@j);
1576             }
1577              
1578 0         0 $sth->finish;
1579 0         0 $sth = $sh->prepare_from_data("describe", \@ti, \@names);
1580 0         0 return $sh->sth_go($sth, 0, NO_RH);
1581             }
1582              
1583              
1584             sub prepare_from_data {
1585 1     1   4 my ($sh, $statement, $data, $names, %attr) = @_;
1586 1         6 my $sponge = DBI->connect("dbi:Sponge:","","",{ RaiseError => 1 });
1587 1         234 my $sth = $sponge->prepare($statement, { rows=>$data, NAME=>$names, %attr });
1588 1         128 return $sth;
1589             }
1590              
1591              
1592             # Do option: sets or gets an option
1593             sub do_option {
1594 48     48   4087 my ($sh, @args) = @_;
1595              
1596 48         80 my $value;
1597 48 100       131 unless (@args) {
1598 1         4 foreach my $opt (sort keys %{ $sh->{options}}) {
  1         14  
1599 26 100       75 $value = (defined $sh->{$opt}) ? $sh->{$opt} : 'undef';
1600 26         86 $sh->log(sprintf("%20s: %s", $opt, $value));
1601             }
1602 1         8 return;
1603             }
1604              
1605 47         71 my $options = Text::Abbrev::abbrev(keys %{$sh->{options}});
  47         401  
1606              
1607             # Expecting the form [option=value] [option=] [option]
1608 47         62458 foreach my $opt (@args) {
1609 47         85 my ($opt_name);
1610 47         342 ($opt_name, $value) = $opt =~ /^\s*(\w+)(?:=(.*))?/;
1611 47 50 33     208 $opt_name = $options->{$opt_name} || $opt_name if $opt_name;
1612 47 50 33     281 if (!$opt_name || !$sh->{options}->{$opt_name}) {
1613 0         0 $sh->log("Unknown or ambiguous option name '$opt_name'");
1614 0         0 next;
1615             }
1616 47 100       149 my $crnt = (defined $sh->{$opt_name}) ? $sh->{$opt_name} : 'undef';
1617 47 100       164 if (not defined $value) {
1618 5         37 $sh->log("/option $opt_name=$crnt");
1619 5         32 $value = $crnt;
1620             }
1621             else {
1622             # Need to deal with quoted strings.
1623             # 1 while ( $value =~ s/[^\\]?["']//g ); #"'
1624             $sh->log("/option $opt_name=$value (was $crnt)")
1625 42 50       114 unless $sh->{batch};
1626 42 50       147 $sh->{$opt_name} = ($value eq 'undef' ) ? undef : $value;
1627             }
1628             }
1629 47 50       1085 return (defined $value ? $value : undef);
1630             }
1631              
1632             #
1633             # Do prompt: sets or gets a prompt
1634             #
1635             sub do_prompt {
1636 0     0   0 my ($sh, @args) = @_;
1637              
1638 0 0       0 return $sh->log( $sh->{prompt} ) unless (@args);
1639 0         0 return $sh->{prompt} = join( '', @args );
1640             }
1641              
1642              
1643             sub do_table_info {
1644 1     1   5 my ($sh, @args) = @_;
1645 1         21 my $dbh = $sh->{dbh};
1646 1         10 my $sth = $dbh->table_info(@args);
1647 1 50       3519 unless(ref $sth) {
1648 0         0 $sh->log("Driver has not implemented the table_info() method, ",
1649             "trying tables()\n");
1650 0         0 my @tables = $dbh->tables(@args); # else try list context
1651 0 0       0 unless (@tables) {
1652 0         0 $sh->print_buffer_nop ("No tables exist ",
1653             "(or driver hasn't implemented the tables method)\n");
1654 0         0 return;
1655             }
1656             $sth = $sh->prepare_from_data("tables",
1657 0         0 [ map { [ $_ ] } @tables ],
  0         0  
1658             [ "TABLE_NAME" ]
1659             );
1660             }
1661 1         11 return $sh->sth_go($sth, 0, NO_RH);
1662             }
1663              
1664             # Support functions.
1665 3     3   14 sub is_spooling ( ) { return shift->{spooling} }
1666 1     1   4 sub spool_on ( ) { return shift->{spooling} = 1 }
1667 1     1   3 sub spool_off ( ) { return shift->{spooling} = 0 }
1668              
1669             1;
1670             __END__