File Coverage

blib/lib/DBI/Shell.pm
Criterion Covered Total %
statement 423 795 53.2
branch 124 390 31.7
condition 25 109 22.9
subroutine 68 91 74.7
pod 1 3 33.3
total 641 1388 46.1


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