| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
############################################################################## |
|
2
|
|
|
|
|
|
|
# Purpose : SQL Shell API |
|
3
|
|
|
|
|
|
|
# Author : John Alden |
|
4
|
|
|
|
|
|
|
# Created : Jul 2006 (refactored from sqlsh.pl) |
|
5
|
|
|
|
|
|
|
# CVS : $Header: /home/cvs/software/cvsroot/db_utils/lib/SQL/Shell.pm,v 1.14 2006/12/05 14:31:33 andreww Exp $ |
|
6
|
|
|
|
|
|
|
############################################################################### |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package SQL::Shell; |
|
9
|
|
|
|
|
|
|
|
|
10
|
1
|
|
|
1
|
|
25721
|
use strict; |
|
|
1
|
|
|
|
|
4
|
|
|
|
1
|
|
|
|
|
30
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
1
|
|
|
1
|
|
4
|
use Carp; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
60
|
|
|
13
|
1
|
|
|
1
|
|
1565
|
use DBI; |
|
|
1
|
|
|
|
|
18362
|
|
|
|
1
|
|
|
|
|
60
|
|
|
14
|
1
|
|
|
1
|
|
6
|
use File::Path; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
63
|
|
|
15
|
1
|
|
|
1
|
|
475
|
use IO::File; |
|
|
1
|
|
|
|
|
8331
|
|
|
|
1
|
|
|
|
|
102
|
|
|
16
|
1
|
|
|
1
|
|
479
|
use URI::Escape; |
|
|
1
|
|
|
|
|
1378
|
|
|
|
1
|
|
|
|
|
57
|
|
|
17
|
|
|
|
|
|
|
|
|
18
|
1
|
|
|
1
|
|
7
|
use vars qw($VERSION); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
102
|
|
|
19
|
|
|
|
|
|
|
$VERSION = ('$Revision: 1.17 $' =~ /([\d\._]+)/)[0]; |
|
20
|
|
|
|
|
|
|
|
|
21
|
1
|
|
50
|
1
|
|
6
|
use constant HISTORY_SIZE => $ENV{HISTSIZE} || $ENV{HISTFILESIZE} || 50; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
82
|
|
|
22
|
1
|
|
|
1
|
|
6
|
use vars qw(%Renderers %Commands %Settings); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
7258
|
|
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
#Available rendering routines |
|
25
|
|
|
|
|
|
|
%Renderers = ( |
|
26
|
|
|
|
|
|
|
'delimited' => \&_render_delimited, |
|
27
|
|
|
|
|
|
|
'box' => \&_render_box, |
|
28
|
|
|
|
|
|
|
'spaced' => \&_render_spaced, |
|
29
|
|
|
|
|
|
|
'record' => \&_render_record, |
|
30
|
|
|
|
|
|
|
'sql' => \&_render_sql, |
|
31
|
|
|
|
|
|
|
'xml' => \&_render_xml, |
|
32
|
|
|
|
|
|
|
); |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
#Commands available by default |
|
35
|
|
|
|
|
|
|
%Commands = ( |
|
36
|
|
|
|
|
|
|
qr/^(list|show) +drivers$/i => \&show_drivers, |
|
37
|
|
|
|
|
|
|
qr/^(?:list|show) datasources (\w+)$/i => \&show_datasources, |
|
38
|
|
|
|
|
|
|
qr/^(show )?history$/i => \&show_history, |
|
39
|
|
|
|
|
|
|
qr/^clear history$/i => \&clear_history, |
|
40
|
|
|
|
|
|
|
qr/^load history from ([\w\-\.\/~]+)$/i => \&load_history, |
|
41
|
|
|
|
|
|
|
qr/^save history to ([\w\-\.\/~]+)$/i => \&save_history, |
|
42
|
|
|
|
|
|
|
qr/^connect (\S+) ?(\S+)? ?(\S+)?/i => \&connect, |
|
43
|
|
|
|
|
|
|
qr/^disconnect$/i => \&disconnect, |
|
44
|
|
|
|
|
|
|
qr/^show +\$dbh +(.*)/i => \&show_dbh, |
|
45
|
|
|
|
|
|
|
qr/^(list|show) +schema$/i => \&show_schema, |
|
46
|
|
|
|
|
|
|
qr/^(list|show) +tablecounts$/i => \&show_tablecounts, |
|
47
|
|
|
|
|
|
|
qr/^(list|show) +(tables|catalogs|schemas|tabletypes)(?: like)?( .*)?$/i => \&show_objects, |
|
48
|
|
|
|
|
|
|
qr/^(list|show) +charsets$/i => \&show_charsets, |
|
49
|
|
|
|
|
|
|
qr/^(list|show) +settings$/i => \&show_settings, |
|
50
|
|
|
|
|
|
|
qr/^(?:desc|describe) +(.*)/i => \&describe, |
|
51
|
|
|
|
|
|
|
qr/^((?:select|explain|recv)\s+.*)/is => \&run_query, |
|
52
|
|
|
|
|
|
|
qr/^((?:create|alter|drop|insert|replace|update|delete|grant|revoke|send) .*)/is => \&do_sql, |
|
53
|
|
|
|
|
|
|
qr/^begin work/i => \&begin_work, |
|
54
|
|
|
|
|
|
|
qr/^rollback/i => \&rollback, |
|
55
|
|
|
|
|
|
|
qr/^commit/i => \&commit, |
|
56
|
|
|
|
|
|
|
qr/^wipe(?: all)? tables$/i => \&wipe_tables, |
|
57
|
|
|
|
|
|
|
qr/^load ([^\s]+) into ([\w\-\.\/]+)(?: delimited by (\S+))?(?: (uri-decode))?(?: from (\S+))?(?: to (\S+))?/i => \&load_data, |
|
58
|
|
|
|
|
|
|
qr/^dump (.+) into ([\w\-\.\/~]+)(?: delimited by (\S+))?/i => \&dump_data, |
|
59
|
|
|
|
|
|
|
qr/^set +(.*?)\s+(.*)/i => \&set_param, |
|
60
|
|
|
|
|
|
|
qr/^(?:execute|source) +(.*)/i => \&run_script, |
|
61
|
|
|
|
|
|
|
qr/^no log$/i => \&disable_logging, |
|
62
|
|
|
|
|
|
|
qr/^log +(.*?) +(?:(?:to|into) +)?(.*)/i => \&enable_logging, |
|
63
|
|
|
|
|
|
|
); |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
%Settings = map {$_ => 1} qw(GetHistory SetHistory AddHistory MaxHistory Interactive Verbose NULL Renderer Logger Delimiter Width LogLevel EscapeStrategy AutoCommit LongTruncOk LongReadLen MultiLine); |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
my %viewable_settings = ( |
|
68
|
|
|
|
|
|
|
'auto-commit' => 'AutoCommit', |
|
69
|
|
|
|
|
|
|
delimiter => 'Delimiter', |
|
70
|
|
|
|
|
|
|
'enter-whitespace' => 'EnterWhitespace', |
|
71
|
|
|
|
|
|
|
'escape' => 'EscapeStrategy', |
|
72
|
|
|
|
|
|
|
longreadlen => 'LongReadLen', |
|
73
|
|
|
|
|
|
|
longtruncok => 'LongTruncOk', |
|
74
|
|
|
|
|
|
|
multiline => 'MultiLine', |
|
75
|
|
|
|
|
|
|
verbose => 'Verbose', |
|
76
|
|
|
|
|
|
|
width => 'Width', |
|
77
|
|
|
|
|
|
|
); |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
my %boolean_settings = map {$_ => 1} qw (AutoCommit LongTruncOk MultiLine Verbose); |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
####################################################################### |
|
82
|
|
|
|
|
|
|
# |
|
83
|
|
|
|
|
|
|
# Public methods - these should croak on error |
|
84
|
|
|
|
|
|
|
# |
|
85
|
|
|
|
|
|
|
####################################################################### |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
sub new |
|
88
|
|
|
|
|
|
|
{ |
|
89
|
1
|
|
|
1
|
1
|
404
|
my ($class, $overrides) = @_; |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
#Default storage for history information (used by closures) |
|
92
|
1
|
|
|
|
|
2
|
my @history; |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
#Default settings |
|
95
|
|
|
|
|
|
|
my $settings = { |
|
96
|
|
|
|
|
|
|
Interactive => $overrides->{Interactive} || 0, |
|
97
|
|
|
|
|
|
|
Verbose => $overrides->{Verbose} || 0, |
|
98
|
|
|
|
|
|
|
Renderer => _renderer($overrides->{Renderer}) || \&_render_box, |
|
99
|
|
|
|
|
|
|
Logger => _renderer($overrides->{Logger}) || \&_render_delimited, |
|
100
|
|
|
|
|
|
|
Delimiter => $overrides->{Delimiter} || "\t", |
|
101
|
|
|
|
|
|
|
Width => $overrides->{Width} || 80, |
|
102
|
|
|
|
|
|
|
MaxHistory => $overrides->{MaxHistory} || HISTORY_SIZE, |
|
103
|
|
|
|
|
|
|
LogLevel => $overrides->{LogLevel}, |
|
104
|
|
|
|
|
|
|
AutoCommit => $overrides->{AutoCommit} || 0, |
|
105
|
|
|
|
|
|
|
LongTruncOk => exists $overrides->{LongTruncOk}? $overrides->{LongTruncOk} : 1, |
|
106
|
|
|
|
|
|
|
LongReadLen => $overrides->{LongReadLen} || 512, |
|
107
|
|
|
|
|
|
|
MultiLine => $overrides->{MultiLine} || 0, |
|
108
|
3
|
|
|
3
|
|
329
|
GetHistory => $overrides->{GetHistory} || sub {return \@history}, |
|
109
|
3
|
|
|
3
|
|
325
|
SetHistory => $overrides->{SetHistory} || sub {my $n = shift; @history = @$n}, |
|
|
3
|
|
|
|
|
10
|
|
|
110
|
9
|
|
|
9
|
|
21
|
AddHistory => $overrides->{AddHistory} || sub {push @history, shift()}, |
|
111
|
1
|
50
|
50
|
|
|
11
|
NULL => exists $overrides->{NULL}? $overrides->{NULL} : 'NULL', |
|
|
|
50
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
112
|
|
|
|
|
|
|
}; |
|
113
|
|
|
|
|
|
|
|
|
114
|
1
|
|
|
|
|
16
|
my %commands = %Commands; |
|
115
|
1
|
|
|
|
|
6
|
my %renderers = %Renderers; |
|
116
|
|
|
|
|
|
|
|
|
117
|
1
|
|
|
|
|
4
|
my $self = { |
|
118
|
|
|
|
|
|
|
'settings' => $settings, |
|
119
|
|
|
|
|
|
|
'commands' => \%commands, |
|
120
|
|
|
|
|
|
|
'renderers' => \%renderers, |
|
121
|
|
|
|
|
|
|
'current_statement' => '' |
|
122
|
|
|
|
|
|
|
}; |
|
123
|
1
|
|
|
|
|
4
|
return bless($self, $class); |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub DESTROY |
|
127
|
|
|
|
|
|
|
{ |
|
128
|
1
|
|
|
1
|
|
1215
|
my $self = shift; |
|
129
|
1
|
50
|
|
|
|
4
|
if(_is_connected($self->{dbh})) { |
|
130
|
0
|
|
|
|
|
0
|
$self->{dbh}->disconnect(); |
|
131
|
|
|
|
|
|
|
} |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub set |
|
135
|
|
|
|
|
|
|
{ |
|
136
|
0
|
|
|
0
|
1
|
0
|
my ($self, $key, $value) = @_; |
|
137
|
0
|
0
|
|
|
|
0
|
croak("Unknown setting: $key") unless $Settings{$key}; |
|
138
|
0
|
|
|
|
|
0
|
$self->{settings}{$key} = $value; |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub get |
|
142
|
|
|
|
|
|
|
{ |
|
143
|
2
|
|
|
2
|
1
|
1311
|
my ($self, $key) = @_; |
|
144
|
2
|
50
|
|
|
|
8
|
croak("Unknown setting: $key") unless $Settings{$key}; |
|
145
|
2
|
|
|
|
|
5
|
return $self->{settings}{$key}; |
|
146
|
|
|
|
|
|
|
} |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
sub install_renderers |
|
149
|
|
|
|
|
|
|
{ |
|
150
|
0
|
|
|
0
|
1
|
0
|
my ($self, $renderers) = @_; |
|
151
|
0
|
0
|
|
|
|
0
|
croak "install_renderers method should be passed a hashref" unless(ref $renderers eq 'HASH'); |
|
152
|
0
|
|
|
|
|
0
|
foreach my $k (keys %$renderers) { |
|
153
|
0
|
|
|
|
|
0
|
$self->{renderers}{$k} = $renderers->{$k}; |
|
154
|
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub uninstall_renderers |
|
158
|
|
|
|
|
|
|
{ |
|
159
|
0
|
|
|
0
|
1
|
0
|
my ($self, $renderers) = @_; |
|
160
|
0
|
0
|
|
|
|
0
|
$renderers = $self->{renderers} unless defined ($renderers); |
|
161
|
0
|
0
|
|
|
|
0
|
croak "uninstall_renderers method should be passed an arrayref" unless(ref $renderers eq 'ARRAY'); |
|
162
|
0
|
|
|
|
|
0
|
for(@$renderers) { |
|
163
|
0
|
0
|
|
|
|
0
|
delete $self->{renderers}{$_} or carp("$_ not found in list of renderers"); |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
} |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
sub install_cmds |
|
168
|
|
|
|
|
|
|
{ |
|
169
|
1
|
|
|
1
|
1
|
1313
|
my ($self, $cmds) = @_; |
|
170
|
1
|
50
|
|
|
|
6
|
croak "install_commands method should be passed a hashref" unless(ref $cmds eq 'HASH'); |
|
171
|
1
|
|
|
|
|
5
|
foreach my $rx(keys %$cmds) { |
|
172
|
1
|
|
|
|
|
4
|
$self->{commands}{$rx} = $cmds->{$rx}; |
|
173
|
|
|
|
|
|
|
} |
|
174
|
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub uninstall_cmds |
|
177
|
|
|
|
|
|
|
{ |
|
178
|
1
|
|
|
1
|
1
|
396
|
my ($self, $cmds) = @_; |
|
179
|
1
|
50
|
|
|
|
4
|
$cmds = $self->{commands} unless defined ($cmds); |
|
180
|
1
|
50
|
|
|
|
4
|
croak "uninstall_commands method should be passed an arrayref" unless(ref $cmds eq 'ARRAY'); |
|
181
|
1
|
|
|
|
|
4
|
for(@$cmds) { |
|
182
|
1
|
50
|
|
|
|
14
|
delete $self->{commands}{$_} or carp("$_ not found in list of commands"); |
|
183
|
|
|
|
|
|
|
} |
|
184
|
|
|
|
|
|
|
} |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub execute_cmd |
|
187
|
|
|
|
|
|
|
{ |
|
188
|
33
|
|
|
33
|
1
|
125737
|
my $self = shift; |
|
189
|
33
|
|
|
|
|
110
|
return $self->_execute(@_); |
|
190
|
|
|
|
|
|
|
} |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub is_connected |
|
193
|
|
|
|
|
|
|
{ |
|
194
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
195
|
0
|
|
|
|
|
0
|
return _is_connected($self->{dbh}); |
|
196
|
|
|
|
|
|
|
} |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub dsn |
|
199
|
|
|
|
|
|
|
{ |
|
200
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
201
|
0
|
0
|
|
|
|
0
|
return undef unless _is_connected($self->{dbh}); |
|
202
|
0
|
|
|
|
|
0
|
return sprintf "DBI:%s:%s", $self->{dbh}{Driver}{Name}, $self->{dbh}{Name}; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
sub render_rowset { |
|
206
|
1
|
|
|
1
|
1
|
2
|
my $self = shift; |
|
207
|
1
|
|
|
|
|
3
|
$self->{settings}{Renderer}->($self, \*STDOUT, @_); |
|
208
|
|
|
|
|
|
|
} |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub log_rowset { |
|
211
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
212
|
0
|
|
|
|
|
0
|
$self->{settings}{Logger}->($self, $self->{LogFH}, @_); |
|
213
|
|
|
|
|
|
|
} |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
############################################### |
|
216
|
|
|
|
|
|
|
# |
|
217
|
|
|
|
|
|
|
# Commands - these should die with /n on error |
|
218
|
|
|
|
|
|
|
# |
|
219
|
|
|
|
|
|
|
############################################### |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub run_script |
|
222
|
|
|
|
|
|
|
{ |
|
223
|
1
|
|
|
1
|
1
|
8
|
my ($self, $script) = @_; |
|
224
|
1
|
50
|
|
|
|
33
|
print "Executing $script\n" if ($self->{settings}{Verbose}); |
|
225
|
1
|
|
|
|
|
9
|
$script = _expand_filename($script); |
|
226
|
1
|
50
|
|
|
|
11
|
my $file = new IO::File "$script" or die("Unable to open file $script - $!"); |
|
227
|
1
|
|
|
|
|
154
|
my @cmds = map {chomp; $_} <$file>; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
8
|
|
|
228
|
1
|
|
|
|
|
9
|
foreach(@cmds) |
|
229
|
|
|
|
|
|
|
{ |
|
230
|
2
|
100
|
|
|
|
9
|
$self->execute_cmd($_) or die("Command '$_' failed - aborting $script"); |
|
231
|
|
|
|
|
|
|
} |
|
232
|
0
|
|
|
|
|
0
|
return 1; |
|
233
|
|
|
|
|
|
|
} |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub load_history |
|
236
|
|
|
|
|
|
|
{ |
|
237
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
|
238
|
1
|
|
50
|
|
|
4
|
my $filename = shift || die("You must specify a file to load the history from"); |
|
239
|
|
|
|
|
|
|
|
|
240
|
1
|
|
|
|
|
5
|
TRACE("Loading history from $filename"); |
|
241
|
1
|
|
|
|
|
4
|
my $history = _load_history($filename); |
|
242
|
1
|
50
|
|
|
|
6
|
$self->{settings}{SetHistory}->($history) if(defined $history); |
|
243
|
1
|
|
|
|
|
4
|
return $history; |
|
244
|
|
|
|
|
|
|
} |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub clear_history |
|
247
|
|
|
|
|
|
|
{ |
|
248
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
|
249
|
1
|
|
|
|
|
3
|
TRACE("Clearing history"); |
|
250
|
1
|
|
|
|
|
3
|
$self->{settings}{SetHistory}->([]); |
|
251
|
1
|
|
|
|
|
4
|
return 1; |
|
252
|
|
|
|
|
|
|
} |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
sub save_history |
|
255
|
|
|
|
|
|
|
{ |
|
256
|
1
|
|
|
1
|
1
|
21
|
my $self = shift; |
|
257
|
1
|
|
50
|
|
|
5
|
my $filename = shift || die("You must specify a file to save the history to"); |
|
258
|
1
|
|
33
|
|
|
15
|
my $max_size = shift || $self->{settings}{MaxHistory}; |
|
259
|
|
|
|
|
|
|
|
|
260
|
1
|
|
|
|
|
4
|
my $history = $self->{settings}{GetHistory}->(); |
|
261
|
1
|
|
|
|
|
7
|
TRACE("Saving history to $filename (contains ".(scalar @$history)." items)"); |
|
262
|
1
|
|
|
|
|
4
|
_save_history($history, $filename, $max_size); |
|
263
|
1
|
|
|
|
|
7
|
return 1; |
|
264
|
|
|
|
|
|
|
} |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
sub show_history |
|
267
|
|
|
|
|
|
|
{ |
|
268
|
1
|
|
|
1
|
1
|
2
|
my $self = shift; |
|
269
|
1
|
|
|
|
|
4
|
my $history = $self->{settings}{GetHistory}->(); |
|
270
|
1
|
|
|
|
|
3
|
print "\n",(map {" ".$_."\n"} @$history),"\n"; |
|
|
3
|
|
|
|
|
31
|
|
|
271
|
1
|
|
|
|
|
9
|
return 1; |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
sub enable_logging |
|
275
|
|
|
|
|
|
|
{ |
|
276
|
0
|
|
|
0
|
1
|
0
|
my ($self, $level, $file) = @_; |
|
277
|
0
|
0
|
|
|
|
0
|
die("Unrecognised logging level: $level\n") unless($level =~ /^(commands|queries|all)$/); |
|
278
|
0
|
|
|
|
|
0
|
my $settings = $self->{settings}; |
|
279
|
0
|
|
|
|
|
0
|
$file = _expand_filename($file); |
|
280
|
0
|
0
|
|
|
|
0
|
$self->{LogFH} = new IO::File ">> $file" or die("Unable to open $file for logging - $!\n"); |
|
281
|
0
|
|
|
|
|
0
|
$settings->{LogLevel} = $level; |
|
282
|
0
|
0
|
|
|
|
0
|
print "Logging $level to $file\n" if($settings->{Verbose}); |
|
283
|
0
|
|
|
|
|
0
|
return 1; |
|
284
|
|
|
|
|
|
|
} |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
sub disable_logging |
|
287
|
|
|
|
|
|
|
{ |
|
288
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
|
289
|
0
|
|
|
|
|
0
|
my $settings = $self->{settings}; |
|
290
|
0
|
0
|
0
|
|
|
0
|
print "Stopped logging $settings->{LogLevel}\n" if($settings->{Verbose} && defined $self->{LogFH}); |
|
291
|
0
|
|
|
|
|
0
|
$self->{LogFH} = undef; |
|
292
|
0
|
|
|
|
|
0
|
$settings->{LogLevel} = undef; |
|
293
|
0
|
|
|
|
|
0
|
return 1; |
|
294
|
|
|
|
|
|
|
} |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub connect |
|
297
|
|
|
|
|
|
|
{ |
|
298
|
0
|
|
|
0
|
1
|
0
|
my($self, $dsn, $username, $password) = @_; |
|
299
|
0
|
|
|
|
|
0
|
my $settings = $self->{settings}; |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
my $dbh = DBI->connect($dsn, $username, $password, |
|
302
|
|
|
|
|
|
|
{PrintError => 0, RaiseError => 1, LongTruncOk => $settings->{LongTruncOk}, |
|
303
|
0
|
|
|
|
|
0
|
LongReadLen => $settings->{LongReadLen}}); |
|
304
|
|
|
|
|
|
|
|
|
305
|
0
|
|
|
|
|
0
|
eval { $dbh->{AutoCommit} = $settings->{AutoCommit} }; |
|
|
0
|
|
|
|
|
0
|
|
|
306
|
0
|
0
|
0
|
|
|
0
|
if ($@ && !$settings->{AutoCommit}) { |
|
307
|
0
|
|
|
|
|
0
|
warn "WARNING: $dsn doesn't appear to support transactions\n"; |
|
308
|
|
|
|
|
|
|
} |
|
309
|
|
|
|
|
|
|
|
|
310
|
0
|
|
|
|
|
0
|
$self->{dbh} = $dbh; |
|
311
|
0
|
|
|
|
|
0
|
return $dbh; |
|
312
|
|
|
|
|
|
|
} |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub disconnect |
|
315
|
|
|
|
|
|
|
{ |
|
316
|
55
|
|
|
55
|
1
|
65
|
my $self = shift; |
|
317
|
55
|
50
|
|
|
|
85
|
$self->{dbh}->disconnect if _is_connected($self->{dbh}); |
|
318
|
55
|
|
|
|
|
80
|
$self->{dbh} = undef; |
|
319
|
55
|
|
|
|
|
78
|
return 1; |
|
320
|
|
|
|
|
|
|
} |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
sub show_charsets |
|
323
|
|
|
|
|
|
|
{ |
|
324
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
|
325
|
0
|
|
|
|
|
0
|
eval {require Locale::Recode}; |
|
|
0
|
|
|
|
|
0
|
|
|
326
|
0
|
0
|
|
|
|
0
|
die "Locale::Recode is not available. Please install it if you want character set support.\n" if($@); |
|
327
|
0
|
|
|
|
|
0
|
my $charsets = Locale::Recode->getSupported(); |
|
328
|
0
|
|
|
|
|
0
|
print "\n",(map {" ".$_."\n"} sort @$charsets),"\n"; |
|
|
0
|
|
|
|
|
0
|
|
|
329
|
0
|
|
|
|
|
0
|
return 1; |
|
330
|
|
|
|
|
|
|
} |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
sub show_drivers |
|
333
|
|
|
|
|
|
|
{ |
|
334
|
2
|
|
|
2
|
1
|
14
|
print "\n",(map {" ".$_."\n"} DBI->available_drivers()),"\n"; |
|
|
14
|
|
|
|
|
1013
|
|
|
335
|
2
|
|
|
|
|
17
|
return 1; |
|
336
|
|
|
|
|
|
|
} |
|
337
|
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
sub show_datasources |
|
339
|
|
|
|
|
|
|
{ |
|
340
|
0
|
|
|
0
|
1
|
0
|
my ($self, $driver) = @_; |
|
341
|
0
|
|
|
|
|
0
|
print "\n",(map {" ".$_."\n"} DBI->data_sources($driver)),"\n"; |
|
|
0
|
|
|
|
|
0
|
|
|
342
|
0
|
|
|
|
|
0
|
return 1; |
|
343
|
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
sub show_dbh |
|
346
|
|
|
|
|
|
|
{ |
|
347
|
1
|
|
|
1
|
1
|
4
|
my ($self, $property) = @_; |
|
348
|
1
|
50
|
|
|
|
4
|
my $dbh = $self->_dbh() or die "Not connected to database.\n"; |
|
349
|
0
|
|
|
|
|
0
|
$self->render_rowset([$property], [[$dbh->{$property}]]); |
|
350
|
0
|
|
|
|
|
0
|
return 1; |
|
351
|
|
|
|
|
|
|
} |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
sub show_schema |
|
354
|
|
|
|
|
|
|
{ |
|
355
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
356
|
0
|
0
|
|
|
|
0
|
my $dbh = $self->_dbh() or die "Not connected to database.\n"; |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
#Banner |
|
359
|
0
|
|
|
|
|
0
|
my($driver, $db, $user) = ($dbh->{Driver}{Name}, $dbh->{Name}, $dbh->{Username}); |
|
360
|
0
|
|
|
|
|
0
|
my $header = ["Schema dump"]; |
|
361
|
0
|
|
|
|
|
0
|
my @data = ( |
|
362
|
|
|
|
|
|
|
["$driver database $db"], |
|
363
|
|
|
|
|
|
|
["connected as $user"], |
|
364
|
|
|
|
|
|
|
["on ".localtime()], |
|
365
|
|
|
|
|
|
|
); |
|
366
|
0
|
|
|
|
|
0
|
$self->_render_box(\*STDOUT, $header, \@data); |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
#Each table |
|
369
|
0
|
|
|
|
|
0
|
foreach(_list_tables($dbh)) |
|
370
|
|
|
|
|
|
|
{ |
|
371
|
0
|
|
|
|
|
0
|
print "\n"; |
|
372
|
0
|
|
|
|
|
0
|
$self->_desc_table($_); |
|
373
|
|
|
|
|
|
|
} |
|
374
|
|
|
|
|
|
|
|
|
375
|
0
|
|
|
|
|
0
|
return 1; |
|
376
|
|
|
|
|
|
|
} |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
# Show the viewable settings: |
|
379
|
|
|
|
|
|
|
sub show_settings { |
|
380
|
1
|
|
|
1
|
1
|
2
|
my $self = shift; |
|
381
|
|
|
|
|
|
|
|
|
382
|
1
|
|
|
|
|
3
|
my @header = qw{ PARAMETER VALUE }; |
|
383
|
1
|
|
|
|
|
2
|
my @data; |
|
384
|
1
|
|
|
|
|
7
|
for my $setting (sort keys %viewable_settings) { |
|
385
|
9
|
|
|
|
|
16
|
my $value = $self->{settings}->{ $viewable_settings{$setting} }; |
|
386
|
9
|
100
|
|
|
|
16
|
$value = '' unless defined $value; |
|
387
|
9
|
100
|
|
|
|
17
|
if ( exists($boolean_settings{ $viewable_settings{$setting} }) ) { |
|
388
|
4
|
100
|
|
|
|
10
|
$value = 'on' if $value eq '1'; |
|
389
|
4
|
100
|
|
|
|
9
|
$value = 'off' if $value eq '0'; |
|
390
|
|
|
|
|
|
|
} |
|
391
|
9
|
100
|
|
|
|
14
|
if ( $setting eq 'escape' ) { |
|
392
|
1
|
|
|
|
|
4
|
my $mapping = { |
|
393
|
|
|
|
|
|
|
'ShowWhitespace' => 'show-whitespace', |
|
394
|
|
|
|
|
|
|
'UriEscape' => 'uri-escape', |
|
395
|
|
|
|
|
|
|
'EscapeWhitespace' => 'escape-whitespace', |
|
396
|
|
|
|
|
|
|
'' => 'off' |
|
397
|
|
|
|
|
|
|
}; |
|
398
|
1
|
|
|
|
|
4
|
$value = $mapping->{$value}; |
|
399
|
|
|
|
|
|
|
} |
|
400
|
9
|
|
|
|
|
18
|
push @data, _escape_whitespace([ $setting, $value ]); |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
|
|
403
|
1
|
|
|
|
|
4
|
$self->render_rowset(\@header, \@data); |
|
404
|
|
|
|
|
|
|
} |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
# Show tables, schemas, catalogs, or table-types: |
|
408
|
|
|
|
|
|
|
sub show_objects { |
|
409
|
1
|
|
|
1
|
1
|
4
|
my $self = shift; |
|
410
|
1
|
|
|
|
|
2
|
my $command = shift; |
|
411
|
1
|
|
|
|
|
2
|
my $object = shift; |
|
412
|
1
|
|
|
|
|
2
|
my $pattern = shift; |
|
413
|
|
|
|
|
|
|
|
|
414
|
1
|
50
|
|
|
|
5
|
$pattern = '%' unless defined $pattern; |
|
415
|
|
|
|
|
|
|
|
|
416
|
1
|
50
|
|
|
|
4
|
my $dbh = $self->_dbh() or die "Not connected to database.\n"; |
|
417
|
0
|
|
|
|
|
0
|
my $sth = undef; |
|
418
|
|
|
|
|
|
|
|
|
419
|
0
|
0
|
|
|
|
0
|
if ( $object eq 'catalogs' ){ |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
420
|
0
|
|
|
|
|
0
|
$sth = $dbh->table_info($pattern,'','',''); |
|
421
|
0
|
|
|
|
|
0
|
$self->_list_object_attrib($sth, 'TABLE_CAT'); |
|
422
|
|
|
|
|
|
|
} |
|
423
|
|
|
|
|
|
|
elsif ( $object eq 'schemas' ) { |
|
424
|
0
|
|
|
|
|
0
|
$sth = $dbh->table_info('',$pattern,'',''); |
|
425
|
0
|
|
|
|
|
0
|
$self->_list_object_attrib($sth, 'TABLE_SCHEM'); |
|
426
|
|
|
|
|
|
|
} |
|
427
|
|
|
|
|
|
|
elsif ( $object eq 'tables' ) { |
|
428
|
0
|
0
|
|
|
|
0
|
if ( $pattern eq '%' ) { |
|
429
|
0
|
|
|
|
|
0
|
$sth = $dbh->table_info(); |
|
430
|
|
|
|
|
|
|
} |
|
431
|
|
|
|
|
|
|
else { |
|
432
|
0
|
|
|
|
|
0
|
$sth = $dbh->table_info('','',$pattern,''); |
|
433
|
|
|
|
|
|
|
} |
|
434
|
0
|
|
|
|
|
0
|
$self->_list_object_attrib($sth, 'TABLE_NAME'); |
|
435
|
|
|
|
|
|
|
} |
|
436
|
|
|
|
|
|
|
elsif ( $object eq 'tabletypes' ) { |
|
437
|
0
|
|
|
|
|
0
|
$sth = $dbh->table_info('','','',$pattern); |
|
438
|
0
|
|
|
|
|
0
|
$self->_list_object_attrib($sth, 'TABLE_TYPE'); |
|
439
|
|
|
|
|
|
|
} |
|
440
|
|
|
|
|
|
|
|
|
441
|
0
|
|
|
|
|
0
|
return 1; |
|
442
|
|
|
|
|
|
|
} |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
sub show_tablecounts |
|
445
|
|
|
|
|
|
|
{ |
|
446
|
1
|
|
|
1
|
1
|
4
|
my $self = shift; |
|
447
|
1
|
50
|
|
|
|
4
|
my $dbh = $self->_dbh() or die "Not connected to database.\n"; |
|
448
|
0
|
|
|
|
|
0
|
$self->render_rowset([qw(table rows)], _summarise_tables($dbh)); |
|
449
|
0
|
|
|
|
|
0
|
return 1; |
|
450
|
|
|
|
|
|
|
} |
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
sub describe |
|
453
|
|
|
|
|
|
|
{ |
|
454
|
1
|
|
|
1
|
1
|
4
|
my ($self, $table) = @_; |
|
455
|
1
|
50
|
|
|
|
4
|
my $dbh = $self->_dbh() or die "Not connected to database.\n"; |
|
456
|
|
|
|
|
|
|
|
|
457
|
0
|
|
|
|
|
0
|
$self->_desc_table($table); |
|
458
|
0
|
|
|
|
|
0
|
return 1; |
|
459
|
|
|
|
|
|
|
} |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
sub run_query |
|
462
|
|
|
|
|
|
|
{ |
|
463
|
1
|
|
|
1
|
1
|
5
|
my ($self, $query) = @_; |
|
464
|
1
|
50
|
|
|
|
4
|
my $dbh = $self->_dbh() or die "Not connected to database.\n"; |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
# Remove the "recv" command, as it is not really a SQL keyword: |
|
467
|
|
|
|
|
|
|
# (it is there so we can pull data from non-select commands) |
|
468
|
0
|
0
|
|
|
|
0
|
$query =~ s/^recv\s+//gis if $query =~ m/^recv\s+/gis; |
|
469
|
|
|
|
|
|
|
|
|
470
|
0
|
|
|
|
|
0
|
my $settings = $self->{settings}; |
|
471
|
0
|
|
|
|
|
0
|
my($headers, $data) = $self->_execute_query($query); |
|
472
|
0
|
|
|
|
|
0
|
$self->render_rowset($headers, $data); |
|
473
|
0
|
0
|
0
|
|
|
0
|
if (defined $settings->{LogLevel} && ($settings->{LogLevel} eq 'queries' || $settings->{LogLevel} eq 'all')) { |
|
|
|
|
0
|
|
|
|
|
|
474
|
0
|
|
|
|
|
0
|
$self->log_rowset($headers, $data); |
|
475
|
|
|
|
|
|
|
} |
|
476
|
0
|
|
|
|
|
0
|
return 1; |
|
477
|
|
|
|
|
|
|
} |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
sub do_sql |
|
480
|
|
|
|
|
|
|
{ |
|
481
|
0
|
|
|
0
|
1
|
0
|
my ($self, $statement) = @_; |
|
482
|
0
|
0
|
|
|
|
0
|
my $dbh = $self->_dbh() or die "Not connected to database.\n"; |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
# Remove the "send" command, as it is not really a SQL keyword: |
|
485
|
|
|
|
|
|
|
# (it is there so we can submit commands that would be interpereted by the shell) |
|
486
|
0
|
0
|
|
|
|
0
|
$statement =~ s/^send\s+//gis if $statement =~ m/^send\s+/gis; |
|
487
|
|
|
|
|
|
|
|
|
488
|
0
|
|
|
|
|
0
|
my $rows = $dbh->do($statement); |
|
489
|
0
|
0
|
|
|
|
0
|
$rows = 0 if $rows eq '0E0'; |
|
490
|
|
|
|
|
|
|
|
|
491
|
0
|
|
|
|
|
0
|
my $cmd = (split /\s+/, $statement)[0]; |
|
492
|
0
|
0
|
|
|
|
0
|
my $obj = |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
scalar $cmd =~ /(create|alter|drop)/? ($statement =~ /$1\s+(\S+\s+\S+?)\b/i)[0] |
|
494
|
|
|
|
|
|
|
: $cmd eq 'insert' ? ($statement =~ /into\s+(\S+?)\b/)[0] |
|
495
|
|
|
|
|
|
|
: $cmd eq 'select' ? ($statement =~ /into\s+(\S+?)\b/)[0] |
|
496
|
|
|
|
|
|
|
: $cmd eq 'update' ? ($statement =~/\s+(\S+?)\b/)[0] |
|
497
|
|
|
|
|
|
|
: $cmd eq 'delete' ? ($statement =~/from\s+(\S+?)\b/)[0] |
|
498
|
|
|
|
|
|
|
: '' |
|
499
|
|
|
|
|
|
|
; |
|
500
|
|
|
|
|
|
|
|
|
501
|
0
|
0
|
0
|
|
|
0
|
print "\U$cmd\E $obj: $rows rows affected\n\n" unless($rows == -1 && !$self->{settings}{Verbose}); |
|
502
|
0
|
|
|
|
|
0
|
return 1; |
|
503
|
|
|
|
|
|
|
} |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
sub begin_work |
|
506
|
|
|
|
|
|
|
{ |
|
507
|
1
|
|
|
1
|
1
|
2
|
my $self = shift; |
|
508
|
1
|
50
|
|
|
|
3
|
my $dbh = $self->_dbh() or die "Not connected to database.\n"; |
|
509
|
0
|
|
|
|
|
0
|
$dbh->begin_work; |
|
510
|
0
|
|
|
|
|
0
|
return 1; |
|
511
|
|
|
|
|
|
|
} |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
sub commit |
|
514
|
|
|
|
|
|
|
{ |
|
515
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
|
516
|
1
|
50
|
|
|
|
3
|
my $dbh = $self->_dbh() or die "Not connected to database.\n"; |
|
517
|
0
|
|
|
|
|
0
|
$dbh->commit; |
|
518
|
0
|
|
|
|
|
0
|
return 1; |
|
519
|
|
|
|
|
|
|
} |
|
520
|
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
sub rollback |
|
522
|
|
|
|
|
|
|
{ |
|
523
|
1
|
|
|
1
|
1
|
2
|
my $self = shift; |
|
524
|
1
|
50
|
|
|
|
5
|
my $dbh = $self->_dbh() or die "Not connected to database.\n"; |
|
525
|
0
|
|
|
|
|
0
|
$dbh->rollback; |
|
526
|
0
|
|
|
|
|
0
|
return 1; |
|
527
|
|
|
|
|
|
|
} |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
sub wipe_tables |
|
530
|
|
|
|
|
|
|
{ |
|
531
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
532
|
0
|
0
|
|
|
|
0
|
my $dbh = $self->_dbh() or die "Not connected to database.\n"; |
|
533
|
0
|
|
|
|
|
0
|
my @tables = _list_tables($dbh); |
|
534
|
|
|
|
|
|
|
|
|
535
|
0
|
0
|
|
|
|
0
|
if($self->{settings}{Interactive}) { |
|
536
|
0
|
|
|
|
|
0
|
print "Wipe all data from:\n\n",(map {" ".$_."\n"} @tables),"\nAre you sure you want to do this? (type 'yes' if you are) "; |
|
|
0
|
|
|
|
|
0
|
|
|
537
|
0
|
|
|
|
|
0
|
my $response = ; |
|
538
|
0
|
|
|
|
|
0
|
chomp $response; |
|
539
|
0
|
0
|
|
|
|
0
|
return 0 unless ($response eq 'yes'); |
|
540
|
|
|
|
|
|
|
} |
|
541
|
|
|
|
|
|
|
|
|
542
|
0
|
|
|
|
|
0
|
foreach(@tables) |
|
543
|
|
|
|
|
|
|
{ |
|
544
|
0
|
|
|
|
|
0
|
$dbh->do("delete from $_"); |
|
545
|
|
|
|
|
|
|
} |
|
546
|
0
|
0
|
|
|
|
0
|
print "\nWiped all data in database\n\n" if($self->{settings}{Verbose}); |
|
547
|
0
|
|
|
|
|
0
|
return 1; |
|
548
|
|
|
|
|
|
|
} |
|
549
|
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
sub load_data |
|
551
|
|
|
|
|
|
|
{ |
|
552
|
1
|
|
|
1
|
1
|
5
|
my ($self,$filename, $table, $delimiter, $uri_decode, $cf, $ct) = @_; |
|
553
|
1
|
|
50
|
|
|
3
|
$uri_decode &&= 1; #Force to boolean (concession to command regex) |
|
554
|
1
|
50
|
|
|
|
6
|
$delimiter = $self->{settings}{Delimiter} unless(defined $delimiter); |
|
555
|
1
|
50
|
33
|
|
|
5
|
die "You must supply a character set to recode into!\n" if ($cf && !$ct); |
|
556
|
1
|
50
|
33
|
|
|
7
|
die "You must supply a source character set for recoding\n" if (!$cf && $ct); |
|
557
|
1
|
50
|
33
|
|
|
5
|
if($cf && $ct) { |
|
558
|
0
|
|
|
|
|
0
|
require Locale::Recode; |
|
559
|
0
|
0
|
|
|
|
0
|
die "Unrecognised character set '$cf'\n" if(not Locale::Recode->resolveAlias($cf)); |
|
560
|
0
|
0
|
|
|
|
0
|
die "Unrecognised character set '$ct'\n" if(not Locale::Recode->resolveAlias($ct)); |
|
561
|
|
|
|
|
|
|
} |
|
562
|
1
|
50
|
|
|
|
3
|
my $dbh = $self->_dbh() or die "Not connected to database.\n"; |
|
563
|
|
|
|
|
|
|
|
|
564
|
0
|
0
|
0
|
|
|
0
|
print "Using URI::Decode\n" if ($uri_decode && $self->{settings}{Verbose}); |
|
565
|
0
|
|
|
|
|
0
|
my $recoder; |
|
566
|
0
|
0
|
|
|
|
0
|
if ($cf) { |
|
567
|
0
|
0
|
|
|
|
0
|
print "Recoding characters from $cf to $ct\n" if ($self->{settings}{Verbose}); |
|
568
|
0
|
|
|
|
|
0
|
require Locale::Recode; |
|
569
|
0
|
|
|
|
|
0
|
$recoder = new Locale::Recode('from' => $cf, 'to' => $ct); |
|
570
|
|
|
|
|
|
|
} |
|
571
|
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
#Open file |
|
573
|
0
|
|
|
|
|
0
|
my $file = new IO::File $filename; |
|
574
|
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
#Read headers |
|
576
|
0
|
|
|
|
|
0
|
my $headers = <$file>; chomp $headers; |
|
|
0
|
|
|
|
|
0
|
|
|
577
|
0
|
|
|
|
|
0
|
my @headers = split($delimiter, $headers); |
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
#Build SQL from headers |
|
580
|
0
|
|
|
|
|
0
|
my $sql = "INSERT into $table (".join(",", @headers).") VALUES (".join(",", map{"?"} @headers).")"; |
|
|
0
|
|
|
|
|
0
|
|
|
581
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare_cached($sql); |
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
#Load data from file |
|
584
|
0
|
|
|
|
|
0
|
my $counter = 0; |
|
585
|
0
|
|
|
|
|
0
|
while(<$file>) |
|
586
|
|
|
|
|
|
|
{ |
|
587
|
0
|
|
|
|
|
0
|
chomp; |
|
588
|
0
|
|
|
|
|
0
|
my @row = split($delimiter, $_); |
|
589
|
0
|
0
|
|
|
|
0
|
die "Error: more values in row ".join(",",@row)." than there are headers (".join(",",@headers)."). Aborting load\n" if(scalar @row > scalar @headers); |
|
590
|
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
#Fill in short rows with nulls |
|
592
|
0
|
|
|
|
|
0
|
while(scalar @row < scalar @headers) { |
|
593
|
0
|
|
|
|
|
0
|
push @row, undef; |
|
594
|
|
|
|
|
|
|
} |
|
595
|
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
#Perform encoding conversions |
|
597
|
0
|
0
|
|
|
|
0
|
@row = _recode($recoder, @row) if ($recoder); |
|
598
|
0
|
0
|
|
|
|
0
|
@row = map {uri_unescape($_)} @row if ($uri_decode); |
|
|
0
|
|
|
|
|
0
|
|
|
599
|
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
#Insert data |
|
601
|
0
|
|
|
|
|
0
|
eval { |
|
602
|
0
|
|
|
|
|
0
|
$sth->execute(@row); |
|
603
|
|
|
|
|
|
|
}; |
|
604
|
0
|
0
|
|
|
|
0
|
die("Error executing $sql with params (" . join(",", @row) . ") at line $. in $filename - $@") if($@); |
|
605
|
|
|
|
|
|
|
|
|
606
|
0
|
|
|
|
|
0
|
$counter++; |
|
607
|
|
|
|
|
|
|
} |
|
608
|
|
|
|
|
|
|
|
|
609
|
0
|
0
|
|
|
|
0
|
print "Loaded $counter rows into $table from $filename\n" if($self->{settings}{Verbose}); |
|
610
|
0
|
|
|
|
|
0
|
return 1; |
|
611
|
|
|
|
|
|
|
} |
|
612
|
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
sub dump_data |
|
614
|
|
|
|
|
|
|
{ |
|
615
|
1
|
|
|
1
|
1
|
3
|
my ($self, $source, $filename, $delimiter) = @_; |
|
616
|
1
|
50
|
|
|
|
4
|
my $dbh = $self->_dbh() or die "Not connected to database.\n"; |
|
617
|
0
|
|
|
|
|
0
|
$source =~ s/^\s+//g; $source =~ s/\s+$//g; #Trim any whitespace |
|
|
0
|
|
|
|
|
0
|
|
|
618
|
0
|
0
|
|
|
|
0
|
print "Dumping $source into $filename\n" if($self->{settings}{Verbose}); |
|
619
|
0
|
0
|
|
|
|
0
|
if(lc($source) eq 'all tables') |
|
620
|
|
|
|
|
|
|
{ |
|
621
|
0
|
|
|
|
|
0
|
my $files = $self->_dump_tables($filename, $delimiter); |
|
622
|
0
|
0
|
|
|
|
0
|
print "Dumped ".scalar(@$files)." tables into $filename:\n" if($self->{settings}{Verbose}); |
|
623
|
0
|
|
|
|
|
0
|
print map {" - $_\n"} @$files; |
|
|
0
|
|
|
|
|
0
|
|
|
624
|
|
|
|
|
|
|
} |
|
625
|
|
|
|
|
|
|
else |
|
626
|
|
|
|
|
|
|
{ |
|
627
|
0
|
|
|
|
|
0
|
my $count = $self->_dump_data($source, $filename, $delimiter); |
|
628
|
0
|
0
|
|
|
|
0
|
print "Dumped $count rows into $filename\n" if($self->{settings}{Verbose}); |
|
629
|
|
|
|
|
|
|
} |
|
630
|
0
|
|
|
|
|
0
|
return 1; |
|
631
|
|
|
|
|
|
|
} |
|
632
|
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
sub set_param |
|
634
|
|
|
|
|
|
|
{ |
|
635
|
12
|
|
|
12
|
1
|
27
|
my ($self,$param, $mode) = @_; |
|
636
|
12
|
|
|
|
|
40
|
TRACE("set $param=$mode"); |
|
637
|
12
|
|
|
|
|
20
|
my $settings = $self->{settings}; |
|
638
|
12
|
|
|
|
|
22
|
my $dbh = $self->_dbh; |
|
639
|
|
|
|
|
|
|
|
|
640
|
12
|
|
|
|
|
20
|
my $valid = 1; |
|
641
|
12
|
100
|
|
|
|
70
|
if($param eq 'display-mode') |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
{ |
|
643
|
2
|
100
|
|
|
|
10
|
die sprintf "'$mode' is an invalid value for display-mode. Valid values are %s\n", join(", ", sort keys %{$self->{renderers}}) unless (exists $self->{renderers}{$mode}); |
|
|
1
|
|
|
|
|
15
|
|
|
644
|
1
|
|
|
|
|
4
|
$settings->{Renderer} = $self->{renderers}{$mode}; |
|
645
|
|
|
|
|
|
|
} |
|
646
|
|
|
|
|
|
|
elsif($param eq 'log-mode') |
|
647
|
|
|
|
|
|
|
{ |
|
648
|
1
|
50
|
|
|
|
7
|
die sprintf "'$mode' is an invalid value for log-mode. Valid values are %s\n", join(", ", sort keys %{$self->{renderers}}) unless(exists $self->{renderers}{$mode}); |
|
|
1
|
|
|
|
|
14
|
|
|
649
|
0
|
|
|
|
|
0
|
$settings->{Logger} = $self->{renderers}{$mode}; |
|
650
|
|
|
|
|
|
|
} |
|
651
|
|
|
|
|
|
|
elsif($param eq 'escape') |
|
652
|
|
|
|
|
|
|
{ |
|
653
|
1
|
50
|
|
|
|
14
|
die("'$mode' is an invalid value for escape should be (off, uri-escape, show-whitespace or escape-whitespace)") unless $mode =~ /(uri-escape|show-whitespace|escape-whitespace|off)/; |
|
654
|
0
|
|
|
|
|
0
|
my $mapping = { |
|
655
|
|
|
|
|
|
|
'show-whitespace' => 'ShowWhitespace', |
|
656
|
|
|
|
|
|
|
'uri-escape' => 'UriEscape', |
|
657
|
|
|
|
|
|
|
'escape-whitespace' => 'EscapeWhitespace', |
|
658
|
|
|
|
|
|
|
'off' => undef |
|
659
|
|
|
|
|
|
|
}; |
|
660
|
0
|
|
|
|
|
0
|
$settings->{EscapeStrategy} = $mapping->{$mode}; |
|
661
|
0
|
0
|
|
|
|
0
|
print "Escape set to $mode\n" if($settings->{Verbose}); |
|
662
|
|
|
|
|
|
|
} |
|
663
|
|
|
|
|
|
|
elsif($param eq 'enter-whitespace') |
|
664
|
|
|
|
|
|
|
{ |
|
665
|
1
|
50
|
|
|
|
8
|
my $_onoff = ($mode =~ /^on$/i) ? 1 : ($mode =~ /^off$/i) ? 0 : undef; |
|
|
|
50
|
|
|
|
|
|
|
666
|
1
|
50
|
|
|
|
8
|
die "'$mode' is an invalid value for enter-whitespace (should be 'on' or 'off')\n" unless(defined $_onoff); |
|
667
|
0
|
|
|
|
|
0
|
$settings->{EnterWhitespace} = $_onoff; |
|
668
|
0
|
0
|
|
|
|
0
|
print "Whitespace ".($settings->{EnterWhitespace}?"may":"may not")." be entered as \\n, \\r and \\t\n" if($settings->{Verbose}); |
|
|
|
0
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
} |
|
670
|
|
|
|
|
|
|
elsif($param eq 'delimiter') |
|
671
|
|
|
|
|
|
|
{ |
|
672
|
0
|
|
|
|
|
0
|
$settings->{Delimiter} = $mode; |
|
673
|
0
|
0
|
|
|
|
0
|
print "Delimiter is now '$settings->{Delimiter}'\n" if($settings->{Verbose}); |
|
674
|
|
|
|
|
|
|
} |
|
675
|
|
|
|
|
|
|
elsif($param eq 'width') |
|
676
|
|
|
|
|
|
|
{ |
|
677
|
1
|
50
|
|
|
|
10
|
die "'$mode' is an invalid value for width (should be an integer)\n" unless($mode =~ /^\d+$/); |
|
678
|
0
|
|
|
|
|
0
|
$settings->{Width} = $mode; |
|
679
|
0
|
0
|
|
|
|
0
|
print "Width is now '$settings->{Width}'\n" if($settings->{Verbose}); |
|
680
|
|
|
|
|
|
|
} |
|
681
|
|
|
|
|
|
|
elsif($param eq 'auto-commit') |
|
682
|
|
|
|
|
|
|
{ |
|
683
|
1
|
50
|
|
|
|
27
|
my $_onoff = ($mode =~ /^on$/i) ? 1 : ($mode =~ /^off$/i) ? 0 : undef; |
|
|
|
50
|
|
|
|
|
|
|
684
|
1
|
50
|
|
|
|
10
|
die "'$mode' is an invalid value for auto-commit (should be 'on' or 'off')\n" unless (defined $_onoff); |
|
685
|
0
|
0
|
|
|
|
0
|
eval {$dbh->{AutoCommit} = $_onoff if _is_connected($dbh) }; |
|
|
0
|
|
|
|
|
0
|
|
|
686
|
0
|
0
|
|
|
|
0
|
die "Couldn't set AutoCommit to '$mode' - $@\n" if($@); |
|
687
|
0
|
0
|
|
|
|
0
|
print "AutoCommit is now '\U$mode\E'\n" if($settings->{Verbose}); |
|
688
|
0
|
|
|
|
|
0
|
$settings->{AutoCommit} = $_onoff; |
|
689
|
|
|
|
|
|
|
} |
|
690
|
|
|
|
|
|
|
elsif($param eq 'longreadlen') |
|
691
|
|
|
|
|
|
|
{ |
|
692
|
1
|
50
|
|
|
|
10
|
die "'$mode' is an invalid value for longreadlen (should be an integer)\n" unless($mode =~ /^\d+$/); |
|
693
|
0
|
0
|
|
|
|
0
|
eval { $dbh->{LongReadLen} = $mode if _is_connected($dbh) }; |
|
|
0
|
|
|
|
|
0
|
|
|
694
|
0
|
0
|
|
|
|
0
|
die "Couldn't set LongReadLen to '$mode' - $@\n" if($@); |
|
695
|
0
|
0
|
|
|
|
0
|
print "LongReadLen set to '$mode'\n" if($settings->{Verbose}); |
|
696
|
0
|
|
|
|
|
0
|
$settings->{LongReadLen} = $mode; |
|
697
|
|
|
|
|
|
|
} |
|
698
|
|
|
|
|
|
|
elsif($param eq 'longtruncok') |
|
699
|
|
|
|
|
|
|
{ |
|
700
|
1
|
50
|
|
|
|
9
|
my $_onoff = ($mode =~ /^on$/i) ? 1 : ($mode =~ /^off$/i) ? 0 : undef; |
|
|
|
50
|
|
|
|
|
|
|
701
|
1
|
50
|
|
|
|
9
|
die "'$mode' is an invalid value for longtruncok (should be 'on' or 'off')\n" unless (defined $_onoff); |
|
702
|
0
|
0
|
|
|
|
0
|
eval { $dbh->{LongTruncOk} = $_onoff if _is_connected($dbh) }; |
|
|
0
|
|
|
|
|
0
|
|
|
703
|
0
|
0
|
|
|
|
0
|
die "Couldn't set LongTruncOk to '\U$mode\E'\n - $@" if($@); |
|
704
|
0
|
0
|
|
|
|
0
|
print "LongTruncOk set to '\U$mode\E'\n" if($settings->{Verbose}); |
|
705
|
0
|
|
|
|
|
0
|
$settings->{LongTruncOk} = $_onoff; |
|
706
|
|
|
|
|
|
|
} |
|
707
|
|
|
|
|
|
|
elsif($param eq 'multiline') |
|
708
|
|
|
|
|
|
|
{ |
|
709
|
1
|
50
|
|
|
|
9
|
my $_onoff = ($mode =~ /^on$/i) ? 1 : ($mode =~ /^off$/i) ? 0 : undef; |
|
|
|
50
|
|
|
|
|
|
|
710
|
1
|
50
|
|
|
|
8
|
die "'$mode' is an invalid value for multiline (should be 'on' or 'off')\n" unless (defined $_onoff); |
|
711
|
0
|
|
|
|
|
0
|
$settings->{MultiLine} = $_onoff; |
|
712
|
|
|
|
|
|
|
} |
|
713
|
|
|
|
|
|
|
elsif($param eq 'tracing') |
|
714
|
|
|
|
|
|
|
{ |
|
715
|
1
|
50
|
|
|
|
9
|
if ($mode =~ /^on$/i) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
716
|
0
|
|
|
|
|
0
|
import Log::Trace("print"); |
|
717
|
0
|
0
|
|
|
|
0
|
print "Log::Trace enabled\n" if($settings->{Verbose}); |
|
718
|
|
|
|
|
|
|
} |
|
719
|
|
|
|
|
|
|
elsif ($mode =~ /^off$/i) { |
|
720
|
0
|
|
|
|
|
0
|
import Log::Trace(); |
|
721
|
0
|
0
|
|
|
|
0
|
print "Log::Trace disabled\n" if($settings->{Verbose}); |
|
722
|
|
|
|
|
|
|
} |
|
723
|
|
|
|
|
|
|
elsif ($mode =~ /^deep$/i) { |
|
724
|
0
|
|
|
|
|
0
|
import Log::Trace("print" => {Deep => 1}); |
|
725
|
0
|
0
|
|
|
|
0
|
print "Log::Trace enabled with deep import into modules\n" if($settings->{Verbose}); |
|
726
|
|
|
|
|
|
|
} |
|
727
|
|
|
|
|
|
|
else { |
|
728
|
1
|
|
|
|
|
6
|
die "'$mode' is an invalid value for tracing (should be 'on', 'deep' or 'off')\n"; |
|
729
|
|
|
|
|
|
|
} |
|
730
|
|
|
|
|
|
|
} |
|
731
|
|
|
|
|
|
|
else |
|
732
|
|
|
|
|
|
|
{ |
|
733
|
1
|
|
|
|
|
7
|
die "Unknown parameter '$param' for set command\n"; |
|
734
|
|
|
|
|
|
|
} |
|
735
|
|
|
|
|
|
|
|
|
736
|
1
|
|
|
|
|
7
|
return $valid; |
|
737
|
|
|
|
|
|
|
} |
|
738
|
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
####################################################################### |
|
741
|
|
|
|
|
|
|
# |
|
742
|
|
|
|
|
|
|
# Private methods |
|
743
|
|
|
|
|
|
|
# |
|
744
|
|
|
|
|
|
|
####################################################################### |
|
745
|
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
# |
|
747
|
|
|
|
|
|
|
# Main worker |
|
748
|
|
|
|
|
|
|
# |
|
749
|
|
|
|
|
|
|
sub _execute |
|
750
|
|
|
|
|
|
|
{ |
|
751
|
33
|
|
|
33
|
|
67
|
my($self, $cmd) = @_; |
|
752
|
33
|
|
|
|
|
40
|
my $valid = 1; |
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
#Convenience vars |
|
755
|
33
|
|
|
|
|
64
|
my $dbh = $self->_dbh; |
|
756
|
33
|
|
|
|
|
53
|
my $settings = $self->{settings}; |
|
757
|
|
|
|
|
|
|
|
|
758
|
33
|
0
|
0
|
|
|
67
|
if (defined $settings->{LogLevel} && ($settings->{LogLevel} eq 'all' || $settings->{LogLevel} eq 'commands')) |
|
|
|
|
33
|
|
|
|
|
|
759
|
|
|
|
|
|
|
{ |
|
760
|
0
|
|
|
|
|
0
|
my $log = $self->{LogFH}; |
|
761
|
0
|
|
|
|
|
0
|
my $dont_log = 0; #May want to extend to allow a list of command regexes to be specified "unsuitable for logging" |
|
762
|
0
|
0
|
|
|
|
0
|
print $log "$cmd\n" unless($dont_log); |
|
763
|
|
|
|
|
|
|
} |
|
764
|
|
|
|
|
|
|
|
|
765
|
33
|
50
|
|
|
|
53
|
if ($settings->{MultiLine}) |
|
766
|
|
|
|
|
|
|
{ |
|
767
|
0
|
|
|
|
|
0
|
$self->{current_statement} .= $cmd."\n"; |
|
768
|
0
|
0
|
|
|
|
0
|
return 1 unless $self->{current_statement} =~ /;\s*$/s; |
|
769
|
0
|
|
|
|
|
0
|
$cmd = $self->{current_statement}; |
|
770
|
0
|
|
|
|
|
0
|
$cmd =~ s/\n/ /sg; |
|
771
|
|
|
|
|
|
|
} |
|
772
|
33
|
|
|
|
|
50
|
$self->{current_statement} = ''; |
|
773
|
|
|
|
|
|
|
|
|
774
|
33
|
|
|
|
|
444
|
$cmd =~ s/(?:^\s*|\s*;?\s*$)//g; |
|
775
|
33
|
50
|
|
|
|
88
|
if($settings->{EnterWhitespace}) |
|
776
|
|
|
|
|
|
|
{ |
|
777
|
0
|
|
|
|
|
0
|
$cmd =~ s/\\n/\n/g; |
|
778
|
0
|
|
|
|
|
0
|
$cmd =~ s/\\r/\r/g; |
|
779
|
0
|
|
|
|
|
0
|
$cmd =~ s/\\t/\t/g; |
|
780
|
|
|
|
|
|
|
} |
|
781
|
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
#Command recognition |
|
783
|
33
|
50
|
|
|
|
64
|
if($cmd) |
|
784
|
|
|
|
|
|
|
{ |
|
785
|
|
|
|
|
|
|
#Look for command in command table |
|
786
|
33
|
|
|
|
|
38
|
my $found = 0; |
|
787
|
33
|
|
|
|
|
35
|
foreach my $regex (keys %{$self->{commands}}) { |
|
|
33
|
|
|
|
|
193
|
|
|
788
|
359
|
|
|
|
|
7349
|
my @args = ($cmd =~ $regex); |
|
789
|
359
|
100
|
|
|
|
938
|
if(@args) { |
|
790
|
|
|
|
|
|
|
eval |
|
791
|
31
|
|
|
|
|
74
|
{ |
|
792
|
|
|
|
|
|
|
#Execute command and convert any true return value to 1 |
|
793
|
31
|
|
50
|
|
|
109
|
$valid = $self->{commands}{$regex}->($self, @args) && 1; |
|
794
|
|
|
|
|
|
|
}; |
|
795
|
31
|
100
|
|
|
|
78
|
if($@) { |
|
796
|
22
|
|
|
|
|
530
|
print $@; |
|
797
|
22
|
|
|
|
|
61
|
$valid = 0; |
|
798
|
|
|
|
|
|
|
} |
|
799
|
31
|
|
|
|
|
45
|
$found = 1; |
|
800
|
31
|
|
|
|
|
80
|
last; |
|
801
|
|
|
|
|
|
|
} |
|
802
|
|
|
|
|
|
|
} |
|
803
|
|
|
|
|
|
|
|
|
804
|
33
|
100
|
|
|
|
134
|
if(not $found) { |
|
805
|
2
|
50
|
|
|
|
8
|
my $s = length($cmd)>20? substr($cmd,0,20)."..." : $cmd; |
|
806
|
2
|
|
|
|
|
58
|
warn "Unrecognised command '$s'\n"; |
|
807
|
2
|
|
|
|
|
11
|
$valid = 0; |
|
808
|
|
|
|
|
|
|
} |
|
809
|
|
|
|
|
|
|
} |
|
810
|
|
|
|
|
|
|
|
|
811
|
33
|
100
|
66
|
|
|
215
|
$settings->{AddHistory}->($cmd) if($cmd =~ /\S/ && $valid); #Add command to history |
|
812
|
33
|
|
|
|
|
202
|
return $valid; |
|
813
|
|
|
|
|
|
|
} |
|
814
|
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
####################################################################### |
|
818
|
|
|
|
|
|
|
# |
|
819
|
|
|
|
|
|
|
# Renderers |
|
820
|
|
|
|
|
|
|
# |
|
821
|
|
|
|
|
|
|
####################################################################### |
|
822
|
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
sub _render_delimited |
|
824
|
|
|
|
|
|
|
{ |
|
825
|
0
|
|
|
0
|
|
0
|
my ($self, $fh, $headers, $data) = @_; |
|
826
|
0
|
|
|
|
|
0
|
my $delim = $self->{settings}{Delimiter}; |
|
827
|
0
|
|
|
|
|
0
|
print $fh join($delim, @$headers)."\n"; |
|
828
|
0
|
|
|
|
|
0
|
foreach(@$data) |
|
829
|
|
|
|
|
|
|
{ |
|
830
|
0
|
|
|
|
|
0
|
print $fh join($delim, @$_)."\n"; |
|
831
|
|
|
|
|
|
|
} |
|
832
|
0
|
|
|
|
|
0
|
print $fh "\n"; |
|
833
|
|
|
|
|
|
|
} |
|
834
|
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
sub _render_sql |
|
836
|
|
|
|
|
|
|
{ |
|
837
|
0
|
|
|
0
|
|
0
|
my ($self, $fh, $headers, $data, $table) = @_; |
|
838
|
0
|
|
0
|
|
|
0
|
$table ||= '$table'; |
|
839
|
0
|
|
|
|
|
0
|
my $sql = "INSERT into $table (".join("," , @$headers).") VALUES (%s);\n"; |
|
840
|
0
|
|
|
|
|
0
|
my $settings = $self->{settings}; |
|
841
|
0
|
|
|
|
|
0
|
my $dbh = $self->_dbh; |
|
842
|
0
|
0
|
|
|
|
0
|
local $settings->{NULL} = 'NULL' unless -t $fh; |
|
843
|
0
|
|
|
|
|
0
|
foreach(@$data) |
|
844
|
|
|
|
|
|
|
{ |
|
845
|
|
|
|
|
|
|
my @fields = map{ |
|
846
|
0
|
|
|
|
|
0
|
defined() ? |
|
847
|
|
|
|
|
|
|
DBI::looks_like_number($_) ? $_ : $dbh->quote($_) |
|
848
|
|
|
|
|
|
|
: $settings->{NULL} |
|
849
|
0
|
0
|
|
|
|
0
|
} @$_; |
|
|
|
0
|
|
|
|
|
|
|
850
|
0
|
|
|
|
|
0
|
printf $fh $sql, join(",", @fields); |
|
851
|
|
|
|
|
|
|
} |
|
852
|
0
|
|
|
|
|
0
|
print $fh "\n"; |
|
853
|
|
|
|
|
|
|
} |
|
854
|
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
sub _render_xml |
|
856
|
|
|
|
|
|
|
{ |
|
857
|
0
|
|
|
0
|
|
0
|
my ($self, $fh, $headers, $data) = @_; |
|
858
|
0
|
|
|
|
|
0
|
require CGI; #For its markup escaping routine |
|
859
|
0
|
|
|
|
|
0
|
print $fh "\n"; |
|
860
|
0
|
|
|
|
|
0
|
foreach my $record (@$data) |
|
861
|
|
|
|
|
|
|
{ |
|
862
|
0
|
|
|
|
|
0
|
print $fh "\t\n"; |
|
863
|
|
|
|
|
|
|
print $fh map { |
|
864
|
0
|
|
|
|
|
0
|
my $val = shift @$record; |
|
|
0
|
|
|
|
|
0
|
|
|
865
|
0
|
|
|
|
|
0
|
$val = CGI::escapeHTML($val); |
|
866
|
0
|
|
|
|
|
0
|
"\t\t<$_>$val$_>\n" |
|
867
|
|
|
|
|
|
|
} @$headers; |
|
868
|
0
|
|
|
|
|
0
|
print $fh "\t\n"; |
|
869
|
|
|
|
|
|
|
} |
|
870
|
0
|
|
|
|
|
0
|
print $fh "\n"; |
|
871
|
0
|
|
|
|
|
0
|
print $fh "\n"; |
|
872
|
|
|
|
|
|
|
} |
|
873
|
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
sub _render_box |
|
875
|
|
|
|
|
|
|
{ |
|
876
|
1
|
|
|
1
|
|
3
|
my ($self, $fh, $headers, $data, $table) = @_; |
|
877
|
1
|
|
|
|
|
2
|
my $settings = $self->{settings}; |
|
878
|
1
|
|
|
|
|
3
|
my $widths = _compute_widths($headers,$data); |
|
879
|
1
|
|
|
1
|
|
8
|
use constant LD_H => '-'; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
65
|
|
|
880
|
1
|
|
|
1
|
|
6
|
use constant LD_V => '|'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
53
|
|
|
881
|
1
|
|
|
1
|
|
6
|
use constant LD_X => '+'; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
3023
|
|
|
882
|
1
|
|
|
|
|
2
|
my $line = join(LD_X, map{LD_H x ($_+2)} @$widths); |
|
|
2
|
|
|
|
|
7
|
|
|
883
|
1
|
50
|
|
|
|
13
|
local $settings->{NULL} = 'NULL' unless -t $fh; |
|
884
|
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
#Table |
|
886
|
1
|
50
|
|
|
|
3
|
if($table) { |
|
887
|
0
|
|
|
|
|
0
|
print $fh LD_X . LD_H x (length $line) . LD_X . "\n"; |
|
888
|
0
|
|
|
|
|
0
|
my $str = " " x int(0.5 * (length($line) - length($table))); |
|
889
|
0
|
|
|
|
|
0
|
$str .= $table; |
|
890
|
0
|
|
|
|
|
0
|
$str .= " " x (length($line) - length($str)); |
|
891
|
0
|
|
|
|
|
0
|
print LD_V . $str . LD_V . "\n"; |
|
892
|
|
|
|
|
|
|
} |
|
893
|
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
#Headers |
|
895
|
1
|
|
|
|
|
25
|
print $fh LD_X . $line . LD_X . "\n"; |
|
896
|
1
|
|
|
|
|
4
|
my $str = LD_V; |
|
897
|
1
|
|
|
|
|
5
|
for(my $l = 0; $l<=$#$headers; $l++) |
|
898
|
|
|
|
|
|
|
{ |
|
899
|
2
|
|
|
|
|
10
|
$str .= " " . $headers->[$l] . " " x ($widths->[$l] - length($headers->[$l])) . " " . LD_V; |
|
900
|
|
|
|
|
|
|
} |
|
901
|
1
|
|
|
|
|
12
|
print $fh $str."\n"; |
|
902
|
|
|
|
|
|
|
|
|
903
|
1
|
|
|
|
|
10
|
print $fh LD_X . $line . LD_X . "\n"; |
|
904
|
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
#Data |
|
906
|
1
|
|
|
|
|
4
|
foreach my $row (@$data) |
|
907
|
|
|
|
|
|
|
{ |
|
908
|
9
|
|
|
|
|
19
|
my $str = LD_V; |
|
909
|
9
|
|
|
|
|
20
|
for(my $l = 0; $l<=$#$headers; $l++) |
|
910
|
|
|
|
|
|
|
{ |
|
911
|
18
|
|
|
|
|
25
|
my $value = $row->[$l]; |
|
912
|
18
|
|
|
|
|
17
|
my $len_val; |
|
913
|
18
|
50
|
|
|
|
25
|
unless (defined $value) { |
|
914
|
0
|
|
|
|
|
0
|
$value = $settings->{NULL}; |
|
915
|
0
|
|
|
|
|
0
|
$len_val = 4; |
|
916
|
|
|
|
|
|
|
} else { |
|
917
|
18
|
|
|
|
|
18
|
$len_val = length $value; |
|
918
|
|
|
|
|
|
|
} |
|
919
|
18
|
|
|
|
|
50
|
$str .= " " . $value . " " x ($widths->[$l] - $len_val) . " " . LD_V; |
|
920
|
|
|
|
|
|
|
} |
|
921
|
9
|
|
|
|
|
82
|
print $fh $str."\n"; |
|
922
|
|
|
|
|
|
|
} |
|
923
|
|
|
|
|
|
|
|
|
924
|
1
|
|
|
|
|
19
|
print $fh LD_X . $line . LD_X . "\n"; |
|
925
|
|
|
|
|
|
|
} |
|
926
|
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
sub _render_spaced |
|
928
|
|
|
|
|
|
|
{ |
|
929
|
0
|
|
|
0
|
|
0
|
my ($self, $fh, $headers, $data) = @_; |
|
930
|
0
|
|
|
|
|
0
|
my $widths = _compute_widths($headers,$data); |
|
931
|
0
|
|
|
|
|
0
|
my $format = join($self->{settings}{Delimiter}, map{"%".$_."s"} @$widths)."\n"; |
|
|
0
|
|
|
|
|
0
|
|
|
932
|
0
|
|
|
|
|
0
|
TRACE($format); |
|
933
|
0
|
|
|
|
|
0
|
printf $fh ($format, @$headers); |
|
934
|
0
|
|
|
|
|
0
|
foreach(@$data) |
|
935
|
|
|
|
|
|
|
{ |
|
936
|
0
|
0
|
|
|
|
0
|
printf $fh ($format, map {defined() ? $_ : 'NULL'} @$_); |
|
|
0
|
|
|
|
|
0
|
|
|
937
|
|
|
|
|
|
|
} |
|
938
|
0
|
|
|
|
|
0
|
print $fh "\n"; |
|
939
|
|
|
|
|
|
|
} |
|
940
|
|
|
|
|
|
|
|
|
941
|
|
|
|
|
|
|
sub _render_record |
|
942
|
|
|
|
|
|
|
{ |
|
943
|
0
|
|
|
0
|
|
0
|
my ($self, $fh, $headers, $data) = @_; |
|
944
|
0
|
|
|
|
|
0
|
my $settings = $self->{settings}; |
|
945
|
0
|
|
|
|
|
0
|
my $header_width = _max_width($headers); |
|
946
|
0
|
|
|
|
|
0
|
my $line = (LD_H x $settings->{Width})."\n"; |
|
947
|
0
|
0
|
|
|
|
0
|
local $settings->{NULL} = 'NULL' unless -t $fh; |
|
948
|
0
|
|
|
|
|
0
|
foreach my $record (@$data) |
|
949
|
|
|
|
|
|
|
{ |
|
950
|
0
|
|
|
|
|
0
|
print $fh $line; |
|
951
|
0
|
|
|
|
|
0
|
for(my $l = 0; $l<=$#$headers; $l++) |
|
952
|
|
|
|
|
|
|
{ |
|
953
|
0
|
|
|
|
|
0
|
my $heading = $headers->[$l] . " " x ($header_width - length($headers->[$l])) . " " . LD_V . " "; |
|
954
|
0
|
|
|
|
|
0
|
my $str; |
|
955
|
0
|
0
|
|
|
|
0
|
if($settings->{Width} > length($heading)) |
|
956
|
|
|
|
|
|
|
{ |
|
957
|
0
|
|
|
|
|
0
|
my $room = $settings->{Width} - length($heading); |
|
958
|
0
|
0
|
|
|
|
0
|
my $text = defined $record->[$l] ? $record->[$l] : $settings->{NULL}; |
|
959
|
0
|
|
|
|
|
0
|
my $segments = length($text)/$room; |
|
960
|
0
|
|
|
|
|
0
|
for(my $i=0; $i<$segments; $i++) |
|
961
|
|
|
|
|
|
|
{ |
|
962
|
0
|
|
|
|
|
0
|
$str .= $heading . substr($text,$i*$room,$room) . "\n" |
|
963
|
|
|
|
|
|
|
} |
|
964
|
|
|
|
|
|
|
} |
|
965
|
|
|
|
|
|
|
else |
|
966
|
|
|
|
|
|
|
{ |
|
967
|
0
|
|
|
|
|
0
|
$str="Terminal too narrow\n"; |
|
968
|
|
|
|
|
|
|
} |
|
969
|
0
|
|
|
|
|
0
|
print $fh $str; |
|
970
|
|
|
|
|
|
|
} |
|
971
|
0
|
|
|
|
|
0
|
print $fh $line."\n"; |
|
972
|
|
|
|
|
|
|
} |
|
973
|
|
|
|
|
|
|
} |
|
974
|
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
####################################################################### |
|
976
|
|
|
|
|
|
|
# |
|
977
|
|
|
|
|
|
|
# Misc private methods |
|
978
|
|
|
|
|
|
|
# |
|
979
|
|
|
|
|
|
|
####################################################################### |
|
980
|
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
#Dump data to a logfile |
|
982
|
|
|
|
|
|
|
sub _dump_data |
|
983
|
|
|
|
|
|
|
{ |
|
984
|
0
|
|
|
0
|
|
0
|
my($self, $sql, $filename, $delimiter) = @_; |
|
985
|
0
|
|
|
|
|
0
|
my $table; |
|
986
|
0
|
0
|
|
|
|
0
|
unless($sql=~/ /) #If it's just one word treat it as a table name |
|
987
|
|
|
|
|
|
|
{ |
|
988
|
0
|
|
|
|
|
0
|
$table = $sql; |
|
989
|
0
|
|
|
|
|
0
|
$sql = "select * from $table"; #Allow just table name to be passed |
|
990
|
|
|
|
|
|
|
} |
|
991
|
0
|
|
|
|
|
0
|
my ($headers, $data) = $self->_execute_query($sql); |
|
992
|
0
|
|
|
|
|
0
|
$filename = _expand_filename($filename); |
|
993
|
0
|
0
|
|
|
|
0
|
my $fh = new IO::File ">$filename" or die ("Unable to write to $filename - $!"); |
|
994
|
0
|
|
|
|
|
0
|
my $settings = $self->{settings}; |
|
995
|
0
|
|
|
|
|
0
|
my $old_delim = $self->{settings}{Delimiter}; |
|
996
|
0
|
|
|
|
|
0
|
eval { |
|
997
|
0
|
0
|
|
|
|
0
|
$self->{settings}{Delimiter} = $delimiter if($delimiter); |
|
998
|
0
|
|
|
|
|
0
|
$settings->{Logger}->($self, $fh, $headers, $data, $table); |
|
999
|
|
|
|
|
|
|
}; |
|
1000
|
0
|
|
|
|
|
0
|
$self->{settings}{Delimiter} = $old_delim; #restore before raising exception |
|
1001
|
0
|
0
|
|
|
|
0
|
die($@) if($@); #Rethrow exception |
|
1002
|
0
|
|
|
|
|
0
|
return scalar(@$data); |
|
1003
|
|
|
|
|
|
|
} |
|
1004
|
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
#Dump all tables to a directory |
|
1006
|
|
|
|
|
|
|
sub _dump_tables |
|
1007
|
|
|
|
|
|
|
{ |
|
1008
|
0
|
|
|
0
|
|
0
|
my($self, $dir, $delimiter) = @_; |
|
1009
|
0
|
|
|
|
|
0
|
$dir = _expand_filename($dir); |
|
1010
|
0
|
0
|
|
|
|
0
|
mkpath($dir) if(! -e $dir); |
|
1011
|
0
|
|
|
|
|
0
|
my @files; |
|
1012
|
0
|
|
|
|
|
0
|
foreach(_list_tables($self->_dbh)) |
|
1013
|
|
|
|
|
|
|
{ |
|
1014
|
0
|
|
|
|
|
0
|
my $filename = $dir."/".$_.".dat"; |
|
1015
|
0
|
|
|
|
|
0
|
push @files, $filename; |
|
1016
|
0
|
|
|
|
|
0
|
$self->_dump_data($_, $filename, $delimiter); |
|
1017
|
|
|
|
|
|
|
} |
|
1018
|
0
|
|
|
|
|
0
|
return \@files; |
|
1019
|
|
|
|
|
|
|
} |
|
1020
|
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
sub _execute_query |
|
1022
|
|
|
|
|
|
|
{ |
|
1023
|
0
|
|
|
0
|
|
0
|
my ($self, $sql) = @_; |
|
1024
|
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
#Place to hang future logic for memory-saving database cursors |
|
1026
|
0
|
|
|
|
|
0
|
my $class = "Tie::Rowset::InMemory"; |
|
1027
|
0
|
|
|
|
|
0
|
TRACE("Executing $sql using $class"); |
|
1028
|
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
#Get a handle onto the data that looks like an array of arrays |
|
1030
|
0
|
|
|
|
|
0
|
my @data; |
|
1031
|
0
|
|
|
|
|
0
|
my $dbh = $self->_dbh; |
|
1032
|
0
|
|
|
|
|
0
|
tie @data, $class, $dbh, $sql, {Type => 'Array'}; |
|
1033
|
0
|
|
|
|
|
0
|
my $object = tied @data; |
|
1034
|
0
|
|
|
|
|
0
|
my $headers = $object->column_names(); |
|
1035
|
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
#Attach filter for escaping data as it's accessed |
|
1037
|
0
|
|
|
|
|
0
|
my $settings = $self->{settings}; |
|
1038
|
0
|
0
|
|
|
|
0
|
if($settings->{EscapeStrategy} eq "EscapeWhitespace") |
|
1039
|
|
|
|
|
|
|
{ |
|
1040
|
0
|
|
|
|
|
0
|
_escape_whitespace($headers); |
|
1041
|
0
|
|
|
|
|
0
|
$object->filter(\&_escape_whitespace); #install a filter on the tied rowset |
|
1042
|
|
|
|
|
|
|
} |
|
1043
|
0
|
0
|
|
|
|
0
|
if($settings->{EscapeStrategy} eq "ShowWhitespace") |
|
|
|
0
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
{ |
|
1045
|
0
|
|
|
|
|
0
|
_show_whitespace($headers); |
|
1046
|
0
|
|
|
|
|
0
|
$object->filter(\&_show_whitespace); #install a filter on the tied rowset |
|
1047
|
|
|
|
|
|
|
} |
|
1048
|
|
|
|
|
|
|
elsif($settings->{EscapeStrategy} eq "UriEscape") |
|
1049
|
|
|
|
|
|
|
{ |
|
1050
|
0
|
|
|
|
|
0
|
_uri_escape($headers); |
|
1051
|
0
|
|
|
|
|
0
|
$object->filter(\&_uri_escape); #install a filter on the tied rowset |
|
1052
|
|
|
|
|
|
|
} |
|
1053
|
|
|
|
|
|
|
|
|
1054
|
0
|
|
|
|
|
0
|
return($headers, \@data); |
|
1055
|
|
|
|
|
|
|
} |
|
1056
|
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
sub _desc_table |
|
1058
|
|
|
|
|
|
|
{ |
|
1059
|
0
|
|
|
0
|
|
0
|
my ($self, $table) = @_; |
|
1060
|
0
|
|
|
|
|
0
|
my $dbh = $self->_dbh; |
|
1061
|
0
|
|
|
|
|
0
|
my $driver = $dbh->{Driver}->{Name}; |
|
1062
|
0
|
|
|
|
|
0
|
my ($headers, $data); |
|
1063
|
0
|
0
|
|
|
|
0
|
if($driver eq 'mysql') |
|
1064
|
|
|
|
|
|
|
{ |
|
1065
|
0
|
|
|
|
|
0
|
($headers, $data) = $self->_execute_query("desc $table"); |
|
1066
|
|
|
|
|
|
|
} |
|
1067
|
|
|
|
|
|
|
else |
|
1068
|
|
|
|
|
|
|
{ |
|
1069
|
0
|
|
|
|
|
0
|
$data = _deduce_columns($dbh,$table); |
|
1070
|
0
|
|
|
|
|
0
|
$headers=['Field','Type','Null']; |
|
1071
|
|
|
|
|
|
|
} |
|
1072
|
0
|
|
|
|
|
0
|
my $settings = $self->{settings}; |
|
1073
|
0
|
|
|
|
|
0
|
$self->render_rowset($headers, $data, $table); |
|
1074
|
0
|
0
|
0
|
|
|
0
|
$self->log_rowset($headers, $data, $table) if($settings->{LogLevel} eq 'queries' || $settings->{LogLevel} eq 'all'); |
|
1075
|
|
|
|
|
|
|
} |
|
1076
|
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
sub _dbh |
|
1078
|
|
|
|
|
|
|
{ |
|
1079
|
55
|
|
|
55
|
|
84
|
my $self = shift; |
|
1080
|
55
|
50
|
|
|
|
117
|
if(_is_connected($self->{dbh})) { |
|
1081
|
0
|
|
|
|
|
0
|
return $self->{dbh}; |
|
1082
|
|
|
|
|
|
|
} else { |
|
1083
|
55
|
|
|
|
|
109
|
$self->disconnect(); |
|
1084
|
55
|
|
|
|
|
142
|
return undef; |
|
1085
|
|
|
|
|
|
|
} |
|
1086
|
|
|
|
|
|
|
} |
|
1087
|
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
####################################################################### |
|
1089
|
|
|
|
|
|
|
# |
|
1090
|
|
|
|
|
|
|
# Private routines |
|
1091
|
|
|
|
|
|
|
# |
|
1092
|
|
|
|
|
|
|
####################################################################### |
|
1093
|
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
sub _renderer { |
|
1095
|
2
|
|
|
2
|
|
5
|
my $renderer = shift; |
|
1096
|
2
|
50
|
33
|
|
|
5
|
if(defined $renderer && ref $renderer ne 'CODE') { |
|
1097
|
0
|
|
0
|
|
|
0
|
$renderer = $Renderers{$renderer} || die("Unrecognised renderer: $renderer\n"); |
|
1098
|
|
|
|
|
|
|
} |
|
1099
|
2
|
|
|
|
|
44
|
return $renderer; |
|
1100
|
|
|
|
|
|
|
} |
|
1101
|
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
sub _is_connected |
|
1103
|
|
|
|
|
|
|
{ |
|
1104
|
111
|
50
|
33
|
111
|
|
283
|
if(defined $_[0] && ref $_[0] && UNIVERSAL::isa($_[0], 'DBI::db') && $_[0]->ping) { |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1105
|
0
|
|
|
|
|
0
|
return 1; |
|
1106
|
|
|
|
|
|
|
} else { |
|
1107
|
111
|
|
|
|
|
354
|
return 0; |
|
1108
|
|
|
|
|
|
|
} |
|
1109
|
|
|
|
|
|
|
} |
|
1110
|
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
# |
|
1112
|
|
|
|
|
|
|
# Table manipulation |
|
1113
|
|
|
|
|
|
|
# |
|
1114
|
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
#List tables and their size |
|
1116
|
|
|
|
|
|
|
sub _summarise_tables |
|
1117
|
|
|
|
|
|
|
{ |
|
1118
|
0
|
|
|
0
|
|
0
|
my($dbh) = @_; |
|
1119
|
0
|
|
|
|
|
0
|
my @results; |
|
1120
|
0
|
|
|
|
|
0
|
foreach my $table(_list_tables($dbh)) |
|
1121
|
|
|
|
|
|
|
{ |
|
1122
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare("select count(*) from $table"); |
|
1123
|
0
|
|
|
|
|
0
|
$sth->execute(); |
|
1124
|
0
|
|
|
|
|
0
|
my ($rows) = $sth->fetchrow_array(); |
|
1125
|
0
|
|
|
|
|
0
|
push @results,[$table, $rows]; |
|
1126
|
|
|
|
|
|
|
} |
|
1127
|
0
|
|
|
|
|
0
|
return \@results; |
|
1128
|
|
|
|
|
|
|
} |
|
1129
|
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
sub _list_tables |
|
1131
|
|
|
|
|
|
|
{ |
|
1132
|
0
|
|
|
0
|
|
0
|
my($dbh) = @_; |
|
1133
|
0
|
|
|
|
|
0
|
my $driver = $dbh->{Driver}->{Name}; |
|
1134
|
0
|
0
|
|
|
|
0
|
if($driver eq 'Oracle') |
|
1135
|
|
|
|
|
|
|
{ |
|
1136
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare("select table_name from cat where table_type=?"); |
|
1137
|
0
|
|
|
|
|
0
|
$sth->execute('TABLE'); |
|
1138
|
0
|
|
|
|
|
0
|
my $tables = $sth->fetchall_arrayref(); |
|
1139
|
0
|
|
|
|
|
0
|
return map {$_->[0]} @$tables; |
|
|
0
|
|
|
|
|
0
|
|
|
1140
|
|
|
|
|
|
|
} |
|
1141
|
|
|
|
|
|
|
else |
|
1142
|
|
|
|
|
|
|
{ |
|
1143
|
|
|
|
|
|
|
#Generic DBI function |
|
1144
|
0
|
|
|
|
|
0
|
return $dbh->tables(); |
|
1145
|
|
|
|
|
|
|
} |
|
1146
|
|
|
|
|
|
|
} |
|
1147
|
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
|
|
1149
|
|
|
|
|
|
|
sub _deduce_columns |
|
1150
|
|
|
|
|
|
|
{ |
|
1151
|
0
|
|
|
0
|
|
0
|
my ($dbh,$table) = @_; |
|
1152
|
0
|
|
|
|
|
0
|
my $sth = $dbh->prepare("select * from $table where 0=1"); |
|
1153
|
0
|
|
|
|
|
0
|
$sth->execute(); |
|
1154
|
0
|
|
|
|
|
0
|
my @names = @{$sth->{NAME}}; |
|
|
0
|
|
|
|
|
0
|
|
|
1155
|
0
|
|
|
|
|
0
|
my (@types, @nullable); |
|
1156
|
|
|
|
|
|
|
eval |
|
1157
|
0
|
|
|
|
|
0
|
{ |
|
1158
|
0
|
|
|
|
|
0
|
my @null = ("NO","YES",""); |
|
1159
|
0
|
|
|
|
|
0
|
my @type_codes = @{$sth->{TYPE}}; |
|
|
0
|
|
|
|
|
0
|
|
|
1160
|
0
|
|
|
|
|
0
|
my @precision = @{$sth->{PRECISION}}; |
|
|
0
|
|
|
|
|
0
|
|
|
1161
|
0
|
|
|
|
|
0
|
@nullable = map{$null[$_]} @{$sth->{NULLABLE}}; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
1162
|
0
|
|
|
|
|
0
|
$sth->finish; |
|
1163
|
|
|
|
|
|
|
|
|
1164
|
0
|
|
|
|
|
0
|
foreach(@type_codes) |
|
1165
|
|
|
|
|
|
|
{ |
|
1166
|
0
|
|
|
|
|
0
|
my $info = $dbh->type_info($_); |
|
1167
|
0
|
|
|
|
|
0
|
my $type = $info->{TYPE_NAME}; |
|
1168
|
0
|
|
|
|
|
0
|
my $precision = shift @precision; |
|
1169
|
0
|
0
|
|
|
|
0
|
$type.="($precision)" if(defined $precision); |
|
1170
|
0
|
|
|
|
|
0
|
push @types, $type; |
|
1171
|
|
|
|
|
|
|
} |
|
1172
|
|
|
|
|
|
|
}; |
|
1173
|
0
|
|
|
|
|
0
|
my @data = map {[$_, shift @types, shift @nullable]} @names; |
|
|
0
|
|
|
|
|
0
|
|
|
1174
|
0
|
|
|
|
|
0
|
return \@data; |
|
1175
|
|
|
|
|
|
|
} |
|
1176
|
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
# Pull and render attributes from an active statement handle. |
|
1178
|
|
|
|
|
|
|
# A helper routine for show_objects() |
|
1179
|
|
|
|
|
|
|
sub _list_object_attrib { |
|
1180
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
1181
|
0
|
|
|
|
|
0
|
my $sth = shift; |
|
1182
|
0
|
|
|
|
|
0
|
my $attrib = shift; |
|
1183
|
|
|
|
|
|
|
|
|
1184
|
0
|
|
|
|
|
0
|
my @header; |
|
1185
|
|
|
|
|
|
|
my @data; |
|
1186
|
|
|
|
|
|
|
|
|
1187
|
0
|
0
|
|
|
|
0
|
if ( $attrib eq 'TABLE_NAME' ) { |
|
1188
|
0
|
|
|
|
|
0
|
@header = qw{ TABLE_CAT TABLE_SCHEM TABLE_NAME TABLE_TYPE REMARKS }; |
|
1189
|
0
|
|
|
|
|
0
|
while (my $row = $sth->fetchrow_hashref('NAME_uc')) { |
|
1190
|
0
|
|
|
|
|
0
|
my @data_row = map { $row->{$_} } @header; |
|
|
0
|
|
|
|
|
0
|
|
|
1191
|
0
|
|
|
|
|
0
|
push @data, \@data_row; |
|
1192
|
|
|
|
|
|
|
} |
|
1193
|
|
|
|
|
|
|
} |
|
1194
|
|
|
|
|
|
|
else { |
|
1195
|
0
|
|
|
|
|
0
|
@header = ( $attrib ); |
|
1196
|
0
|
|
|
|
|
0
|
my $hash_ref = $sth->fetchall_hashref($attrib); |
|
1197
|
0
|
|
|
|
|
0
|
@data = map { [ $_ ] } sort keys %{ $hash_ref }; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
1198
|
|
|
|
|
|
|
} |
|
1199
|
|
|
|
|
|
|
|
|
1200
|
0
|
|
|
|
|
0
|
$self->render_rowset(\@header, \@data); |
|
1201
|
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
} |
|
1203
|
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
# |
|
1205
|
|
|
|
|
|
|
# History |
|
1206
|
|
|
|
|
|
|
# |
|
1207
|
|
|
|
|
|
|
sub _load_history { |
|
1208
|
1
|
|
|
1
|
|
2
|
my $filename = shift; |
|
1209
|
1
|
|
|
|
|
2
|
local *FH; |
|
1210
|
1
|
|
|
|
|
2
|
my @hist; |
|
1211
|
1
|
50
|
|
|
|
3
|
open (FH, _expand_filename($filename)) or die("Unable to load history from $filename - $!"); |
|
1212
|
1
|
|
|
|
|
16
|
while () { |
|
1213
|
2
|
|
|
|
|
6
|
chomp; push @hist, $_; |
|
|
2
|
|
|
|
|
10
|
|
|
1214
|
|
|
|
|
|
|
} |
|
1215
|
1
|
|
|
|
|
9
|
close FH; |
|
1216
|
1
|
|
|
|
|
14
|
TRACE("Loaded ".scalar @hist." items from $filename"); |
|
1217
|
1
|
|
|
|
|
5
|
return \@hist; |
|
1218
|
|
|
|
|
|
|
} |
|
1219
|
|
|
|
|
|
|
|
|
1220
|
|
|
|
|
|
|
sub _save_history { |
|
1221
|
1
|
|
|
1
|
|
2
|
my $history = shift; |
|
1222
|
1
|
|
50
|
|
|
4
|
my $filename = shift || die("You must specify a file to save the history to"); |
|
1223
|
1
|
|
50
|
|
|
4
|
my $max_size = shift || HISTORY_SIZE; |
|
1224
|
1
|
50
|
|
|
|
3
|
my $max_hist = scalar @$history >= $max_size ? $max_size : scalar @$history; |
|
1225
|
1
|
|
|
|
|
5
|
TRACE("Saving $max_hist items to $filename"); |
|
1226
|
1
|
|
|
|
|
5
|
my @hist = @$history[-$max_hist..-1]; |
|
1227
|
1
|
|
|
|
|
3
|
local *FH; |
|
1228
|
1
|
50
|
|
|
|
5
|
open (FH, "> " . _expand_filename($filename)) or die("Unable to save history to $filename - $!"); |
|
1229
|
1
|
|
|
|
|
11
|
print FH $_, $/ for @hist; |
|
1230
|
1
|
|
|
|
|
31
|
close FH; |
|
1231
|
|
|
|
|
|
|
} |
|
1232
|
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
sub _recode |
|
1234
|
|
|
|
|
|
|
{ |
|
1235
|
0
|
|
|
0
|
|
0
|
my ($recoder, @rows) = @_; |
|
1236
|
0
|
|
|
|
|
0
|
foreach (@rows) |
|
1237
|
|
|
|
|
|
|
{ |
|
1238
|
0
|
|
|
|
|
0
|
my $init = $_; |
|
1239
|
0
|
0
|
|
|
|
0
|
die $recoder->getError if $recoder->getError; |
|
1240
|
0
|
0
|
|
|
|
0
|
$recoder->recode($_) or die $recoder->getError; |
|
1241
|
0
|
|
|
|
|
0
|
TRACE("recoded FROM [$init] to [$_]"); |
|
1242
|
|
|
|
|
|
|
} |
|
1243
|
0
|
|
|
|
|
0
|
return @rows; |
|
1244
|
|
|
|
|
|
|
} |
|
1245
|
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
sub _escape_whitespace |
|
1247
|
|
|
|
|
|
|
{ |
|
1248
|
9
|
|
|
9
|
|
11
|
my $row = shift; |
|
1249
|
9
|
|
|
|
|
12
|
foreach(@$row) |
|
1250
|
|
|
|
|
|
|
{ |
|
1251
|
18
|
|
|
|
|
22
|
s/\r/\\r/g; |
|
1252
|
18
|
|
|
|
|
18
|
s/\n/\\n/g; |
|
1253
|
18
|
|
|
|
|
23
|
s/\t/\\t/g; |
|
1254
|
|
|
|
|
|
|
} |
|
1255
|
9
|
|
|
|
|
17
|
return $row; |
|
1256
|
|
|
|
|
|
|
} |
|
1257
|
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
sub _show_whitespace |
|
1259
|
|
|
|
|
|
|
{ |
|
1260
|
0
|
|
|
0
|
|
0
|
my $row = shift; |
|
1261
|
0
|
|
|
|
|
0
|
$row = _escape_whitespace($row); |
|
1262
|
0
|
|
|
|
|
0
|
foreach(@$row) |
|
1263
|
|
|
|
|
|
|
{ |
|
1264
|
0
|
|
|
|
|
0
|
s/ /./g; #Also convert spaces to dots |
|
1265
|
|
|
|
|
|
|
} |
|
1266
|
0
|
|
|
|
|
0
|
return $row; |
|
1267
|
|
|
|
|
|
|
} |
|
1268
|
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
sub _uri_escape |
|
1270
|
|
|
|
|
|
|
{ |
|
1271
|
0
|
|
|
0
|
|
0
|
my $row = shift; |
|
1272
|
0
|
|
|
|
|
0
|
my @new = map {uri_escape($_)} @$row; |
|
|
0
|
|
|
|
|
0
|
|
|
1273
|
0
|
|
|
|
|
0
|
return \@new; |
|
1274
|
|
|
|
|
|
|
} |
|
1275
|
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
sub _compute_widths |
|
1277
|
|
|
|
|
|
|
{ |
|
1278
|
1
|
|
|
1
|
|
2
|
my ($headers,$data) = @_; |
|
1279
|
1
|
|
|
|
|
3
|
my @widths = map {length $_} @$headers; |
|
|
2
|
|
|
|
|
4
|
|
|
1280
|
1
|
|
|
|
|
3
|
foreach my $row(@$data) |
|
1281
|
|
|
|
|
|
|
{ |
|
1282
|
9
|
|
|
|
|
14
|
for(0..$#widths) |
|
1283
|
|
|
|
|
|
|
{ |
|
1284
|
18
|
50
|
|
|
|
22
|
my $len = defined $row->[$_] ? length($row->[$_]) : length 'NULL'; |
|
1285
|
18
|
100
|
|
|
|
33
|
$widths[$_] = $len if($len > $widths[$_]); |
|
1286
|
|
|
|
|
|
|
} |
|
1287
|
|
|
|
|
|
|
} |
|
1288
|
1
|
|
|
|
|
3
|
return \@widths; |
|
1289
|
|
|
|
|
|
|
} |
|
1290
|
|
|
|
|
|
|
|
|
1291
|
|
|
|
|
|
|
sub _max_width |
|
1292
|
|
|
|
|
|
|
{ |
|
1293
|
0
|
|
|
0
|
|
0
|
my ($list) = @_; |
|
1294
|
0
|
|
|
|
|
0
|
my $width = 0; |
|
1295
|
0
|
|
|
|
|
0
|
foreach (@$list) |
|
1296
|
|
|
|
|
|
|
{ |
|
1297
|
0
|
|
|
|
|
0
|
my $len = length($_); |
|
1298
|
0
|
0
|
|
|
|
0
|
$width = $len if($len > $width); |
|
1299
|
|
|
|
|
|
|
} |
|
1300
|
0
|
|
|
|
|
0
|
return $width; |
|
1301
|
|
|
|
|
|
|
} |
|
1302
|
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
sub _expand_filename { |
|
1305
|
3
|
|
|
3
|
|
7
|
my $file = shift; |
|
1306
|
3
|
50
|
|
|
|
10
|
if ($file =~ s/^~([^\/]*)//) |
|
1307
|
|
|
|
|
|
|
{ |
|
1308
|
0
|
0
|
|
|
|
0
|
my $home = $1 ? ((getpwnam ($1)) [7]) : $ENV{HOME}; |
|
1309
|
0
|
|
|
|
|
0
|
$file = $home . $file; |
|
1310
|
|
|
|
|
|
|
} |
|
1311
|
3
|
|
|
|
|
105
|
return $file; |
|
1312
|
|
|
|
|
|
|
} |
|
1313
|
|
|
|
|
|
|
|
|
1314
|
|
|
|
|
|
|
# stubs for Log::Trace |
|
1315
|
|
|
|
17
|
0
|
|
sub TRACE{} |
|
1316
|
|
|
|
0
|
0
|
|
sub DUMP{} |
|
1317
|
|
|
|
|
|
|
|
|
1318
|
|
|
|
|
|
|
############################################################################################ |
|
1319
|
|
|
|
|
|
|
# |
|
1320
|
|
|
|
|
|
|
# Inlined package for the time being whilst Tie::Rowset is being worked on |
|
1321
|
|
|
|
|
|
|
# |
|
1322
|
|
|
|
|
|
|
############################################################################################ |
|
1323
|
|
|
|
|
|
|
|
|
1324
|
|
|
|
|
|
|
package Tie::Rowset::InMemory; |
|
1325
|
|
|
|
|
|
|
|
|
1326
|
1
|
|
|
1
|
|
8
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
21
|
|
|
1327
|
1
|
|
|
1
|
|
4
|
use Carp; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
776
|
|
|
1328
|
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
############################################## |
|
1330
|
|
|
|
|
|
|
# TIE interface |
|
1331
|
|
|
|
|
|
|
############################################## |
|
1332
|
|
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
sub TIEARRAY |
|
1334
|
|
|
|
|
|
|
{ |
|
1335
|
0
|
|
|
0
|
|
|
my ($class, $dbh, $sql, $options) = @_; |
|
1336
|
0
|
0
|
|
|
|
|
$options = {} unless defined $options; |
|
1337
|
0
|
|
|
|
|
|
my $params = $options->{params}; |
|
1338
|
|
|
|
|
|
|
my $self = { |
|
1339
|
|
|
|
|
|
|
'dbh' => $dbh, |
|
1340
|
|
|
|
|
|
|
'sql' => $sql, |
|
1341
|
|
|
|
|
|
|
'params' => defined $params? $params : [], |
|
1342
|
|
|
|
|
|
|
'type' => $options->{Type} || 'Hash', |
|
1343
|
|
|
|
|
|
|
'filter' => $options->{Filter}, |
|
1344
|
0
|
0
|
0
|
|
|
|
'count' => undef, |
|
1345
|
|
|
|
|
|
|
}; |
|
1346
|
0
|
|
|
|
|
|
bless $self, $class; |
|
1347
|
0
|
|
|
|
|
|
TRACE(__PACKAGE__." constructor"); |
|
1348
|
0
|
|
|
|
|
|
return $self; |
|
1349
|
|
|
|
|
|
|
} |
|
1350
|
|
|
|
|
|
|
|
|
1351
|
|
|
|
|
|
|
sub DESTROY |
|
1352
|
|
|
|
|
|
|
{ |
|
1353
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
1354
|
0
|
0
|
|
|
|
|
$self->{sth}->finish() if defined($self->{sth}); |
|
1355
|
|
|
|
|
|
|
} |
|
1356
|
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
sub FETCH |
|
1358
|
|
|
|
|
|
|
{ |
|
1359
|
0
|
|
|
0
|
|
|
my ($self, $index) = @_; |
|
1360
|
0
|
|
|
|
|
|
TRACE("FETCH $index"); |
|
1361
|
0
|
0
|
|
|
|
|
$self->_execute_query() unless $self->{data}; |
|
1362
|
0
|
0
|
|
|
|
|
croak("index $index is out of bounds - rowset only has " . scalar @{$self->{data}}." elements") if($index+1 > scalar @{$self->{data}}); |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
1363
|
0
|
|
|
|
|
|
my $rv = $self->{data}->[$index]; |
|
1364
|
0
|
0
|
|
|
|
|
$rv = $self->{filter}->($rv) if defined $self->{filter}; #optionally filter |
|
1365
|
0
|
|
|
|
|
|
DUMP("Fetch $index", $rv); |
|
1366
|
0
|
|
|
|
|
|
return $rv; |
|
1367
|
|
|
|
|
|
|
} |
|
1368
|
|
|
|
|
|
|
|
|
1369
|
|
|
|
|
|
|
sub FETCHSIZE |
|
1370
|
|
|
|
|
|
|
{ |
|
1371
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
1372
|
0
|
0
|
|
|
|
|
$self->_execute_query() unless $self->{data}; |
|
1373
|
0
|
|
|
|
|
|
TRACE("Fetch size - " . scalar @{$self->{data}}); |
|
|
0
|
|
|
|
|
|
|
|
1374
|
0
|
|
|
|
|
|
return scalar @{$self->{data}}; |
|
|
0
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
} |
|
1376
|
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
############################################## |
|
1378
|
|
|
|
|
|
|
# Non-tied OO interface (access via tied) |
|
1379
|
|
|
|
|
|
|
############################################## |
|
1380
|
|
|
|
|
|
|
|
|
1381
|
|
|
|
|
|
|
sub column_names |
|
1382
|
|
|
|
|
|
|
{ |
|
1383
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
1384
|
0
|
0
|
|
|
|
|
$self->_execute_query() unless $self->{headers}; |
|
1385
|
0
|
|
|
|
|
|
return $self->{headers}; |
|
1386
|
|
|
|
|
|
|
} |
|
1387
|
|
|
|
|
|
|
|
|
1388
|
|
|
|
|
|
|
sub filter |
|
1389
|
|
|
|
|
|
|
{ |
|
1390
|
0
|
|
|
0
|
|
|
my ($self, $filter) = @_; |
|
1391
|
0
|
0
|
|
|
|
|
$self->{filter} = $filter if defined($filter); |
|
1392
|
0
|
|
|
|
|
|
return $self->{filter}; |
|
1393
|
|
|
|
|
|
|
} |
|
1394
|
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
############################################## |
|
1396
|
|
|
|
|
|
|
# private methods |
|
1397
|
|
|
|
|
|
|
############################################## |
|
1398
|
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
sub _execute_query |
|
1400
|
|
|
|
|
|
|
{ |
|
1401
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
1402
|
|
|
|
|
|
|
eval |
|
1403
|
0
|
|
|
|
|
|
{ |
|
1404
|
0
|
|
|
|
|
|
my $sth = $self->{dbh}->prepare($self->{sql}); |
|
1405
|
0
|
|
|
|
|
|
$sth->execute(@{$self->{params}}); |
|
|
0
|
|
|
|
|
|
|
|
1406
|
0
|
|
|
|
|
|
$self->{headers} = $sth->{NAME}; |
|
1407
|
0
|
0
|
|
|
|
|
if($self->{type} eq 'Array') { |
|
1408
|
0
|
|
|
|
|
|
$self->{data} = $sth->fetchall_arrayref(); |
|
1409
|
|
|
|
|
|
|
} else { |
|
1410
|
0
|
|
|
|
|
|
my @loh; |
|
1411
|
0
|
|
|
|
|
|
while(my $hashref = $sth->fetchrow_hashref) |
|
1412
|
|
|
|
|
|
|
{ |
|
1413
|
0
|
|
|
|
|
|
push @loh, { %$hashref }; |
|
1414
|
|
|
|
|
|
|
} |
|
1415
|
0
|
|
|
|
|
|
$self->{data} = \@loh; |
|
1416
|
|
|
|
|
|
|
} |
|
1417
|
|
|
|
|
|
|
}; |
|
1418
|
0
|
0
|
|
|
|
|
if($@) |
|
1419
|
|
|
|
|
|
|
{ |
|
1420
|
0
|
|
|
|
|
|
$@ =~ s/\n$//; |
|
1421
|
0
|
|
|
|
|
|
die("$@ sql=$self->{sql}"); #Decorate error messages with SQL |
|
1422
|
|
|
|
|
|
|
} |
|
1423
|
|
|
|
|
|
|
} |
|
1424
|
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
# stubs for Log::Trace |
|
1426
|
|
|
|
0
|
|
|
sub TRACE{} |
|
1427
|
|
|
|
0
|
|
|
sub DUMP{} |
|
1428
|
|
|
|
|
|
|
|
|
1429
|
|
|
|
|
|
|
|
|
1430
|
|
|
|
|
|
|
=head1 NAME |
|
1431
|
|
|
|
|
|
|
|
|
1432
|
|
|
|
|
|
|
SQL::Shell - command interpreter for DBI shells |
|
1433
|
|
|
|
|
|
|
|
|
1434
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
1435
|
|
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
use SQL::Shell; |
|
1437
|
|
|
|
|
|
|
|
|
1438
|
|
|
|
|
|
|
#Initialise and configure |
|
1439
|
|
|
|
|
|
|
my $sqlsh = new SQL::Shell(\%settings); |
|
1440
|
|
|
|
|
|
|
$sqlsh->set($setting, $new_value); |
|
1441
|
|
|
|
|
|
|
$value = $sqlsh->get($setting); |
|
1442
|
|
|
|
|
|
|
|
|
1443
|
|
|
|
|
|
|
#Interpret commands |
|
1444
|
|
|
|
|
|
|
$sqlsh->execute_command($command); |
|
1445
|
|
|
|
|
|
|
$sqlsh->run_script($filename); |
|
1446
|
|
|
|
|
|
|
|
|
1447
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
1448
|
|
|
|
|
|
|
|
|
1449
|
|
|
|
|
|
|
SQL::Shell is a command-interpreter API for building shells and batch scripts. |
|
1450
|
|
|
|
|
|
|
A command-line interface with readline support - sqlsh.pl - is included as part of the CPAN distribution. See for a user guide. |
|
1451
|
|
|
|
|
|
|
|
|
1452
|
|
|
|
|
|
|
SQL::Shell offers features similar to the mysql or sql*plus client programs but is database independent. |
|
1453
|
|
|
|
|
|
|
The default command syntax is arguably more user-friendly than dbish not requiring any go, do or slashes to fire SQL statements at the database. |
|
1454
|
|
|
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
Features include: |
|
1456
|
|
|
|
|
|
|
|
|
1457
|
|
|
|
|
|
|
=over 4 |
|
1458
|
|
|
|
|
|
|
|
|
1459
|
|
|
|
|
|
|
=item * issuing common SQL statements by simply typing them |
|
1460
|
|
|
|
|
|
|
|
|
1461
|
|
|
|
|
|
|
=item * command history |
|
1462
|
|
|
|
|
|
|
|
|
1463
|
|
|
|
|
|
|
=item * listing drivers, datasources, tables |
|
1464
|
|
|
|
|
|
|
|
|
1465
|
|
|
|
|
|
|
=item * describing a table or the entire schema |
|
1466
|
|
|
|
|
|
|
|
|
1467
|
|
|
|
|
|
|
=item * dumping and loading data to/from delimited text files |
|
1468
|
|
|
|
|
|
|
|
|
1469
|
|
|
|
|
|
|
=item * character set conversion when loading data |
|
1470
|
|
|
|
|
|
|
|
|
1471
|
|
|
|
|
|
|
=item * logging of queries, results or all commands to file |
|
1472
|
|
|
|
|
|
|
|
|
1473
|
|
|
|
|
|
|
=item * a number of formats for display/logging data (sql, xml, delimited, boxed) |
|
1474
|
|
|
|
|
|
|
|
|
1475
|
|
|
|
|
|
|
=item * executing a series of commands from a file |
|
1476
|
|
|
|
|
|
|
|
|
1477
|
|
|
|
|
|
|
=back |
|
1478
|
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
You can also install custom commands, rendering formats and command history mechanisms. |
|
1480
|
|
|
|
|
|
|
All the commands run by the interpreter are available via the API so if you don't like the default command syntax you can replace the command regexes with your own. |
|
1481
|
|
|
|
|
|
|
|
|
1482
|
|
|
|
|
|
|
It's been developed and used in anger with Oracle and mysql but should work with any database with a DBD:: driver. |
|
1483
|
|
|
|
|
|
|
|
|
1484
|
|
|
|
|
|
|
=head1 METHODS |
|
1485
|
|
|
|
|
|
|
|
|
1486
|
|
|
|
|
|
|
=over 4 |
|
1487
|
|
|
|
|
|
|
|
|
1488
|
|
|
|
|
|
|
=item $sqlsh = new SQL::Shell(\%settings); |
|
1489
|
|
|
|
|
|
|
|
|
1490
|
|
|
|
|
|
|
Constructs a new object and initialises it with a set of settings. |
|
1491
|
|
|
|
|
|
|
See L for a complete list. |
|
1492
|
|
|
|
|
|
|
|
|
1493
|
|
|
|
|
|
|
=item $sqlsh->set($setting, $new_value) |
|
1494
|
|
|
|
|
|
|
|
|
1495
|
|
|
|
|
|
|
Changes a setting once the object has been constructed. |
|
1496
|
|
|
|
|
|
|
See L for a complete list. |
|
1497
|
|
|
|
|
|
|
|
|
1498
|
|
|
|
|
|
|
=item $value = $sqlsh->get($setting) |
|
1499
|
|
|
|
|
|
|
|
|
1500
|
|
|
|
|
|
|
Fetches a setting. |
|
1501
|
|
|
|
|
|
|
See L for a complete list. |
|
1502
|
|
|
|
|
|
|
|
|
1503
|
|
|
|
|
|
|
=back |
|
1504
|
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
=head2 Commands |
|
1506
|
|
|
|
|
|
|
|
|
1507
|
|
|
|
|
|
|
=over 4 |
|
1508
|
|
|
|
|
|
|
|
|
1509
|
|
|
|
|
|
|
=item $sqlsh->execute_cmd($command) |
|
1510
|
|
|
|
|
|
|
|
|
1511
|
|
|
|
|
|
|
Executes a command ($command is a string). |
|
1512
|
|
|
|
|
|
|
|
|
1513
|
|
|
|
|
|
|
Returns 1 if the command was successful. |
|
1514
|
|
|
|
|
|
|
Returns 0 if the command was unsuccessful. |
|
1515
|
|
|
|
|
|
|
|
|
1516
|
|
|
|
|
|
|
=item $sqlsh->run_script($filename) |
|
1517
|
|
|
|
|
|
|
|
|
1518
|
|
|
|
|
|
|
Executes a sequence of commands in a file. |
|
1519
|
|
|
|
|
|
|
Dies if there is a problem. |
|
1520
|
|
|
|
|
|
|
|
|
1521
|
|
|
|
|
|
|
=item $sqlsh->install_cmds(\%additional_commands) |
|
1522
|
|
|
|
|
|
|
|
|
1523
|
|
|
|
|
|
|
%additional_commands should contain a mapping of regex to coderef. |
|
1524
|
|
|
|
|
|
|
See L for more information. |
|
1525
|
|
|
|
|
|
|
|
|
1526
|
|
|
|
|
|
|
=item $sqlsh->uninstall_cmds(\@commands) |
|
1527
|
|
|
|
|
|
|
|
|
1528
|
|
|
|
|
|
|
@additional_commands should contain a list of regexes to remove. |
|
1529
|
|
|
|
|
|
|
If uninstall_cmds is called with no arguments, all commands will be uninstalled. |
|
1530
|
|
|
|
|
|
|
|
|
1531
|
|
|
|
|
|
|
=item $sqlsh->set_param($param, $value) |
|
1532
|
|
|
|
|
|
|
|
|
1533
|
|
|
|
|
|
|
Equivalent to the "set " command. |
|
1534
|
|
|
|
|
|
|
In many cases this will affect the internal settings accessible through the C and C methods. |
|
1535
|
|
|
|
|
|
|
|
|
1536
|
|
|
|
|
|
|
=back |
|
1537
|
|
|
|
|
|
|
|
|
1538
|
|
|
|
|
|
|
=head2 Renderers |
|
1539
|
|
|
|
|
|
|
|
|
1540
|
|
|
|
|
|
|
=over 4 |
|
1541
|
|
|
|
|
|
|
|
|
1542
|
|
|
|
|
|
|
=item $sqlsh->install_renderers(\%additional_renderers) |
|
1543
|
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
%additional_renderers should contain a mapping of renderer name to coderef. |
|
1545
|
|
|
|
|
|
|
See L for more information. |
|
1546
|
|
|
|
|
|
|
|
|
1547
|
|
|
|
|
|
|
=item $sqlsh->uninstall_renderers(\@renderers) |
|
1548
|
|
|
|
|
|
|
|
|
1549
|
|
|
|
|
|
|
@renderers should contain a list of renderer names to remove. |
|
1550
|
|
|
|
|
|
|
If uninstall_renderers is called with no arguments, all renderers will be uninstalled. |
|
1551
|
|
|
|
|
|
|
|
|
1552
|
|
|
|
|
|
|
=item $sqlsh->render_rowset(\@headers, \@data, $table) |
|
1553
|
|
|
|
|
|
|
|
|
1554
|
|
|
|
|
|
|
Calls the current renderer (writes to STDOUT) |
|
1555
|
|
|
|
|
|
|
|
|
1556
|
|
|
|
|
|
|
=item $sqlsh->log_rowset(\@headers, \@data, $table) |
|
1557
|
|
|
|
|
|
|
|
|
1558
|
|
|
|
|
|
|
Calls the current logger |
|
1559
|
|
|
|
|
|
|
|
|
1560
|
|
|
|
|
|
|
=back |
|
1561
|
|
|
|
|
|
|
|
|
1562
|
|
|
|
|
|
|
=head2 Database connection |
|
1563
|
|
|
|
|
|
|
|
|
1564
|
|
|
|
|
|
|
=over 4 |
|
1565
|
|
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
=item $dsn = $sqlsh->connect($dsn, $user, $pass) |
|
1567
|
|
|
|
|
|
|
|
|
1568
|
|
|
|
|
|
|
Connects to a DBI datasource. |
|
1569
|
|
|
|
|
|
|
Equivalent to issuing the "connect $dsn $user $pass" command. |
|
1570
|
|
|
|
|
|
|
|
|
1571
|
|
|
|
|
|
|
=item $sqlsh->disconnect() |
|
1572
|
|
|
|
|
|
|
|
|
1573
|
|
|
|
|
|
|
Disconnects if connected. |
|
1574
|
|
|
|
|
|
|
Equivalent to issuing the "disconnect" command. |
|
1575
|
|
|
|
|
|
|
|
|
1576
|
|
|
|
|
|
|
=item $bool = $sqlsh->is_connected() |
|
1577
|
|
|
|
|
|
|
|
|
1578
|
|
|
|
|
|
|
Check if we're connected to the database. |
|
1579
|
|
|
|
|
|
|
|
|
1580
|
|
|
|
|
|
|
=item $string = $sqlsh->dsn() |
|
1581
|
|
|
|
|
|
|
|
|
1582
|
|
|
|
|
|
|
The datasource we're currently connected as - undef if not connected. |
|
1583
|
|
|
|
|
|
|
|
|
1584
|
|
|
|
|
|
|
=back |
|
1585
|
|
|
|
|
|
|
|
|
1586
|
|
|
|
|
|
|
=head2 History manipulation |
|
1587
|
|
|
|
|
|
|
|
|
1588
|
|
|
|
|
|
|
=over 4 |
|
1589
|
|
|
|
|
|
|
|
|
1590
|
|
|
|
|
|
|
=item $arrayref = $sqlsh->load_history($filename) |
|
1591
|
|
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
Loads a sequence of commands from a file into the command history. |
|
1593
|
|
|
|
|
|
|
Equivalent to "load history from $filename". |
|
1594
|
|
|
|
|
|
|
|
|
1595
|
|
|
|
|
|
|
=item $sqlsh->clear_history() |
|
1596
|
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
Clears the command history. |
|
1598
|
|
|
|
|
|
|
Equivalent to "clear history". |
|
1599
|
|
|
|
|
|
|
|
|
1600
|
|
|
|
|
|
|
=item $sqlsh->save_history($filename, $size) |
|
1601
|
|
|
|
|
|
|
|
|
1602
|
|
|
|
|
|
|
Saves the command history to a file in a format suitable for C and C. |
|
1603
|
|
|
|
|
|
|
Equivalent to "save history to $filename", except the maximum number of items can be specified. |
|
1604
|
|
|
|
|
|
|
$size is optional - if not specified defaults to the MaxHistory setting. |
|
1605
|
|
|
|
|
|
|
|
|
1606
|
|
|
|
|
|
|
=item $sqlsh->show_history() |
|
1607
|
|
|
|
|
|
|
|
|
1608
|
|
|
|
|
|
|
Displays the command history. |
|
1609
|
|
|
|
|
|
|
Equivalent to "show history". |
|
1610
|
|
|
|
|
|
|
|
|
1611
|
|
|
|
|
|
|
=back |
|
1612
|
|
|
|
|
|
|
|
|
1613
|
|
|
|
|
|
|
=head2 Logging |
|
1614
|
|
|
|
|
|
|
|
|
1615
|
|
|
|
|
|
|
=over 4 |
|
1616
|
|
|
|
|
|
|
|
|
1617
|
|
|
|
|
|
|
=item $sqlsh->enable_logging($level, $file) |
|
1618
|
|
|
|
|
|
|
|
|
1619
|
|
|
|
|
|
|
Enables logging to a file. |
|
1620
|
|
|
|
|
|
|
$level should be all, queries or commands. |
|
1621
|
|
|
|
|
|
|
Equivalent to "log $level $file". |
|
1622
|
|
|
|
|
|
|
|
|
1623
|
|
|
|
|
|
|
=item $sqlsh->disable_logging() |
|
1624
|
|
|
|
|
|
|
|
|
1625
|
|
|
|
|
|
|
Disables logging to a file. |
|
1626
|
|
|
|
|
|
|
Equivalent to "no log". |
|
1627
|
|
|
|
|
|
|
|
|
1628
|
|
|
|
|
|
|
=back |
|
1629
|
|
|
|
|
|
|
|
|
1630
|
|
|
|
|
|
|
=head2 Querying |
|
1631
|
|
|
|
|
|
|
|
|
1632
|
|
|
|
|
|
|
=over 4 |
|
1633
|
|
|
|
|
|
|
|
|
1634
|
|
|
|
|
|
|
=item $sqlsh->show_drivers() |
|
1635
|
|
|
|
|
|
|
|
|
1636
|
|
|
|
|
|
|
Outputs a list of database drivers. Equivalent to "show drivers". |
|
1637
|
|
|
|
|
|
|
|
|
1638
|
|
|
|
|
|
|
=item $sqlsh->show_datasources($driver) |
|
1639
|
|
|
|
|
|
|
|
|
1640
|
|
|
|
|
|
|
Outputs a list of datasources for a driver. Equivalent to "show datasources $driver". |
|
1641
|
|
|
|
|
|
|
|
|
1642
|
|
|
|
|
|
|
=item $sqlsh->show_dbh($property) |
|
1643
|
|
|
|
|
|
|
|
|
1644
|
|
|
|
|
|
|
Outputs a property of a database handle. Equivalent to "show \$dbh $property". |
|
1645
|
|
|
|
|
|
|
|
|
1646
|
|
|
|
|
|
|
=item $sqlsh->show_schema() |
|
1647
|
|
|
|
|
|
|
|
|
1648
|
|
|
|
|
|
|
Equivalent to "show schema". |
|
1649
|
|
|
|
|
|
|
|
|
1650
|
|
|
|
|
|
|
=item $sqlsh->show_objects() |
|
1651
|
|
|
|
|
|
|
|
|
1652
|
|
|
|
|
|
|
Displays a list of tables, schemas, catalogs or table-types depending on the |
|
1653
|
|
|
|
|
|
|
object argument passed. |
|
1654
|
|
|
|
|
|
|
|
|
1655
|
|
|
|
|
|
|
=item $sqlsh->show_tablecounts() |
|
1656
|
|
|
|
|
|
|
|
|
1657
|
|
|
|
|
|
|
Displays a list of tables with row counts. Equivalent to "show tablecounts". |
|
1658
|
|
|
|
|
|
|
|
|
1659
|
|
|
|
|
|
|
=item $sqlsh->show_settings() |
|
1660
|
|
|
|
|
|
|
|
|
1661
|
|
|
|
|
|
|
Displays a list of internal C settings. Equivalent to "show |
|
1662
|
|
|
|
|
|
|
settings". Not all internal settings are included here yet. |
|
1663
|
|
|
|
|
|
|
|
|
1664
|
|
|
|
|
|
|
=item $sqlsh->describe($table) |
|
1665
|
|
|
|
|
|
|
|
|
1666
|
|
|
|
|
|
|
Displays the columns in the table. Equivalent to "describe $table". |
|
1667
|
|
|
|
|
|
|
|
|
1668
|
|
|
|
|
|
|
=item $sqlsh->run_query($sql) |
|
1669
|
|
|
|
|
|
|
|
|
1670
|
|
|
|
|
|
|
Displays the rowset returned by the query. Equivalent to execute_cmd with a select or explain statement. |
|
1671
|
|
|
|
|
|
|
|
|
1672
|
|
|
|
|
|
|
=back |
|
1673
|
|
|
|
|
|
|
|
|
1674
|
|
|
|
|
|
|
=head2 Modifying data |
|
1675
|
|
|
|
|
|
|
|
|
1676
|
|
|
|
|
|
|
=over 4 |
|
1677
|
|
|
|
|
|
|
|
|
1678
|
|
|
|
|
|
|
=item $sqlsh->do_sql($sql) |
|
1679
|
|
|
|
|
|
|
|
|
1680
|
|
|
|
|
|
|
Executes a SQL statement that modifies the database. Equivalent to execute_cmd with a DML or DDL statement. |
|
1681
|
|
|
|
|
|
|
|
|
1682
|
|
|
|
|
|
|
=item $sqlsh->begin_work() |
|
1683
|
|
|
|
|
|
|
|
|
1684
|
|
|
|
|
|
|
Starts a transaction. Equivalent to "begin work". |
|
1685
|
|
|
|
|
|
|
|
|
1686
|
|
|
|
|
|
|
=item $sqlsh->commit() |
|
1687
|
|
|
|
|
|
|
|
|
1688
|
|
|
|
|
|
|
Commits a transaction. Equivalent to "commit". |
|
1689
|
|
|
|
|
|
|
|
|
1690
|
|
|
|
|
|
|
=item $sqlsh->rollback() |
|
1691
|
|
|
|
|
|
|
|
|
1692
|
|
|
|
|
|
|
Rolls back a transaction. Equivalent to "rollback". |
|
1693
|
|
|
|
|
|
|
|
|
1694
|
|
|
|
|
|
|
=item $sqlsh->wipe_tables() |
|
1695
|
|
|
|
|
|
|
|
|
1696
|
|
|
|
|
|
|
Blanks all the tables in the database. |
|
1697
|
|
|
|
|
|
|
Will prompt for confirmation if the Interactive setting is enabled. |
|
1698
|
|
|
|
|
|
|
Equivalent to "wipe tables". |
|
1699
|
|
|
|
|
|
|
|
|
1700
|
|
|
|
|
|
|
=back |
|
1701
|
|
|
|
|
|
|
|
|
1702
|
|
|
|
|
|
|
=head2 Loading and dumping data |
|
1703
|
|
|
|
|
|
|
|
|
1704
|
|
|
|
|
|
|
=over 4 |
|
1705
|
|
|
|
|
|
|
|
|
1706
|
|
|
|
|
|
|
=item $sqlsh->dump_data($source, $filename, $delimiter) |
|
1707
|
|
|
|
|
|
|
|
|
1708
|
|
|
|
|
|
|
Dumps data from a table or query into a delimited file. |
|
1709
|
|
|
|
|
|
|
$source should either be a table name or a select query. |
|
1710
|
|
|
|
|
|
|
This is equivalent to the "dump data" command. |
|
1711
|
|
|
|
|
|
|
|
|
1712
|
|
|
|
|
|
|
=item $sqlsh->load_data($filename, $table, $delimiter, $uri_decode, $charset_from, $charset_to) |
|
1713
|
|
|
|
|
|
|
|
|
1714
|
|
|
|
|
|
|
Loads data from a delimited file into a database table. |
|
1715
|
|
|
|
|
|
|
$uri_decode is a boolean value - if true the data will be URI-decoded before being inserted. |
|
1716
|
|
|
|
|
|
|
$charset_from and $charset_to are character set names understood by Locale::Recode. |
|
1717
|
|
|
|
|
|
|
This is equivalent to the "load data" command. |
|
1718
|
|
|
|
|
|
|
|
|
1719
|
|
|
|
|
|
|
=item $sqlsh->show_charsets() |
|
1720
|
|
|
|
|
|
|
|
|
1721
|
|
|
|
|
|
|
Lists the character sets supported by the recoding feature of "load data". Equivalent to "show charsets". |
|
1722
|
|
|
|
|
|
|
|
|
1723
|
|
|
|
|
|
|
=back |
|
1724
|
|
|
|
|
|
|
|
|
1725
|
|
|
|
|
|
|
=head1 CUSTOMISING |
|
1726
|
|
|
|
|
|
|
|
|
1727
|
|
|
|
|
|
|
=head2 INSTALLING CUSTOM COMMANDS |
|
1728
|
|
|
|
|
|
|
|
|
1729
|
|
|
|
|
|
|
The coderef will be passed the $sqlsh object followed by each argument captured by the regex. |
|
1730
|
|
|
|
|
|
|
|
|
1731
|
|
|
|
|
|
|
my %additional_commands = ( |
|
1732
|
|
|
|
|
|
|
qr/^hello from (\.*)/ => sub { |
|
1733
|
|
|
|
|
|
|
my ($self, $name) = @_; |
|
1734
|
|
|
|
|
|
|
print "hi there $name\n"; |
|
1735
|
|
|
|
|
|
|
}); |
|
1736
|
|
|
|
|
|
|
|
|
1737
|
|
|
|
|
|
|
To install this: |
|
1738
|
|
|
|
|
|
|
|
|
1739
|
|
|
|
|
|
|
$sqlsh->install_cmds(\%additional_commands) |
|
1740
|
|
|
|
|
|
|
|
|
1741
|
|
|
|
|
|
|
Then in sqlsh: |
|
1742
|
|
|
|
|
|
|
|
|
1743
|
|
|
|
|
|
|
> hello from John |
|
1744
|
|
|
|
|
|
|
hi there John |
|
1745
|
|
|
|
|
|
|
|
|
1746
|
|
|
|
|
|
|
=head2 INSTALLING CUSTOM RENDERERS |
|
1747
|
|
|
|
|
|
|
|
|
1748
|
|
|
|
|
|
|
Renderers are coderefs which are passed the following arguments: |
|
1749
|
|
|
|
|
|
|
|
|
1750
|
|
|
|
|
|
|
$sqlsh - the SQL::Shell object |
|
1751
|
|
|
|
|
|
|
$fh - the filehandle to render to |
|
1752
|
|
|
|
|
|
|
$headers - an arrayref of column headings |
|
1753
|
|
|
|
|
|
|
$data - an arrayref of arrays containing the data (row major) |
|
1754
|
|
|
|
|
|
|
$table - the name of the table being rendered (not defined in all contexts) |
|
1755
|
|
|
|
|
|
|
|
|
1756
|
|
|
|
|
|
|
Here's an example to render data in CSV format: |
|
1757
|
|
|
|
|
|
|
|
|
1758
|
|
|
|
|
|
|
sub my_renderer { |
|
1759
|
|
|
|
|
|
|
my ($sqlsh, $fh, $headers, $data, $table) = @_; |
|
1760
|
|
|
|
|
|
|
my $delim = ","; |
|
1761
|
|
|
|
|
|
|
print $fh "#Dump of $table" if($table); #Assuming our CSV format support #-style comments |
|
1762
|
|
|
|
|
|
|
print $fh join($delim, @$headers)."\n"; |
|
1763
|
|
|
|
|
|
|
foreach my $row (@$data) |
|
1764
|
|
|
|
|
|
|
{ |
|
1765
|
|
|
|
|
|
|
print $fh join($delim, @$row)."\n"; |
|
1766
|
|
|
|
|
|
|
} |
|
1767
|
|
|
|
|
|
|
} |
|
1768
|
|
|
|
|
|
|
|
|
1769
|
|
|
|
|
|
|
To install this: |
|
1770
|
|
|
|
|
|
|
|
|
1771
|
|
|
|
|
|
|
$sqlsh->install_renderers({'csv' => \&my_renderer}); |
|
1772
|
|
|
|
|
|
|
|
|
1773
|
|
|
|
|
|
|
Then in sqlsh: |
|
1774
|
|
|
|
|
|
|
|
|
1775
|
|
|
|
|
|
|
> set display-mode csv |
|
1776
|
|
|
|
|
|
|
|
|
1777
|
|
|
|
|
|
|
=head2 INSTALLING A CUSTOM HISTORY MECHANISM |
|
1778
|
|
|
|
|
|
|
|
|
1779
|
|
|
|
|
|
|
You can install a custom history recording mechanism by overriding the GetHistory, SetHistory and AddHistory callbacks which should take the following arguments and return values: |
|
1780
|
|
|
|
|
|
|
|
|
1781
|
|
|
|
|
|
|
=over 4 |
|
1782
|
|
|
|
|
|
|
|
|
1783
|
|
|
|
|
|
|
=item $arrayref = $GetHistorySub->() |
|
1784
|
|
|
|
|
|
|
|
|
1785
|
|
|
|
|
|
|
=item $SetHistorySub->($arrayref) |
|
1786
|
|
|
|
|
|
|
|
|
1787
|
|
|
|
|
|
|
=item $AddHistorySub->($string) |
|
1788
|
|
|
|
|
|
|
|
|
1789
|
|
|
|
|
|
|
=back |
|
1790
|
|
|
|
|
|
|
|
|
1791
|
|
|
|
|
|
|
An example: |
|
1792
|
|
|
|
|
|
|
|
|
1793
|
|
|
|
|
|
|
my $term = new Term::ReadLine "My Shell"; |
|
1794
|
|
|
|
|
|
|
my $autohistory = $term->Features()->{autohistory}; |
|
1795
|
|
|
|
|
|
|
my $sqlsh = new SQL::Shell({ |
|
1796
|
|
|
|
|
|
|
'GetHistory' => sub {[$term->GetHistory()]}); |
|
1797
|
|
|
|
|
|
|
'SetHistory' => sub {my $history = shift; $term->SetHistory(@$history)}); |
|
1798
|
|
|
|
|
|
|
'AddHistory' => sub {my $cmd = shift; $term->addhistory($cmd) if !$autohistory}); |
|
1799
|
|
|
|
|
|
|
}); |
|
1800
|
|
|
|
|
|
|
|
|
1801
|
|
|
|
|
|
|
=head1 SETTINGS |
|
1802
|
|
|
|
|
|
|
|
|
1803
|
|
|
|
|
|
|
The following settings can only be set through the constructor or the C method: |
|
1804
|
|
|
|
|
|
|
|
|
1805
|
|
|
|
|
|
|
NAME DESCRIPTION DEFAULT |
|
1806
|
|
|
|
|
|
|
GetHistory Callback to fetch history sub {return \@history} |
|
1807
|
|
|
|
|
|
|
SetHistory Callback to set history sub {my $n = shift; @history = @$n} |
|
1808
|
|
|
|
|
|
|
AddHistory Callback to add cmd to history sub {push @history, shift()} |
|
1809
|
|
|
|
|
|
|
MaxHistory Maximum length of history to save $ENV{HISTSIZE} || $ENV{HISTFILESIZE} || 50 |
|
1810
|
|
|
|
|
|
|
Interactive Should SQL::Shell ask questions? 0 |
|
1811
|
|
|
|
|
|
|
Verbose Should SQL::Shell print messages? 0 |
|
1812
|
|
|
|
|
|
|
NULL How to display null values NULL |
|
1813
|
|
|
|
|
|
|
|
|
1814
|
|
|
|
|
|
|
The following are also affected by the C method or the "set" command: |
|
1815
|
|
|
|
|
|
|
|
|
1816
|
|
|
|
|
|
|
NAME DESCRIPTION DEFAULT |
|
1817
|
|
|
|
|
|
|
Renderer Current renderer for screen \&_render_box |
|
1818
|
|
|
|
|
|
|
Logger Current renderer for logfile \&_render_delimited |
|
1819
|
|
|
|
|
|
|
Delimiter Delimiter for delimited format \t |
|
1820
|
|
|
|
|
|
|
Width Width used for record display 80 |
|
1821
|
|
|
|
|
|
|
LogLevel Log what? all|commands|queries undef |
|
1822
|
|
|
|
|
|
|
EscapeStrategy UriEscape|EscapeWhitespace|ShowWhitespace undef |
|
1823
|
|
|
|
|
|
|
AutoCommit Commit each statement 0 |
|
1824
|
|
|
|
|
|
|
LongTruncOk OK to truncate LONG datatypes? 1 |
|
1825
|
|
|
|
|
|
|
LongReadLen Amount read from LONG datatypes 512 |
|
1826
|
|
|
|
|
|
|
MultiLine Allows multiline sql statements 0 |
|
1827
|
|
|
|
|
|
|
|
|
1828
|
|
|
|
|
|
|
=head1 COMMANDS |
|
1829
|
|
|
|
|
|
|
|
|
1830
|
|
|
|
|
|
|
show drivers |
|
1831
|
|
|
|
|
|
|
show datasources |
|
1832
|
|
|
|
|
|
|
connect [ ] - connect to DBI DSN |
|
1833
|
|
|
|
|
|
|
disconnect - disconnect from the DB |
|
1834
|
|
|
|
|
|
|
|
|
1835
|
|
|
|
|
|
|
show tables - display a list of tables |
|
1836
|
|
|
|
|
|
|
show catalogs - display a list of catalogs |
|
1837
|
|
|
|
|
|
|
show schemas - display a list of schemas |
|
1838
|
|
|
|
|
|
|
show tabletypes - display a list of tabletypes |
|
1839
|
|
|
|
|
|
|
show schema - display the entire schema |
|
1840
|
|
|
|
|
|
|
show settings - display some internal settings |
|
1841
|
|
|
|
|
|
|
desc - display schema of table
|
1842
|
|
|
|
|
|
|
|
|
1843
|
|
|
|
|
|
|
show $dbh - show a database handle object. |
|
1844
|
|
|
|
|
|
|
some examples: |
|
1845
|
|
|
|
|
|
|
show $dbh Name |
|
1846
|
|
|
|
|
|
|
show $dbh LongReadLen |
|
1847
|
|
|
|
|
|
|
show $dbh mysql_serverinfo (mysql only) |
|
1848
|
|
|
|
|
|
|
|
|
1849
|
|
|
|
|
|
|
set display-mode delimited|spaced|box|record|sql|xml - query display mode |
|
1850
|
|
|
|
|
|
|
set log-mode delimited|spaced|box|record|sql|xml - set the query log mode |
|
1851
|
|
|
|
|
|
|
set delimiter - set the column delimiter (default is tab) |
|
1852
|
|
|
|
|
|
|
set escape show-whitespace|escape-whitespace|uri-escape|off |
|
1853
|
|
|
|
|
|
|
- show-whitespace is just for looking at |
|
1854
|
|
|
|
|
|
|
- escape-whitespace is compatible with enter-whitespace |
|
1855
|
|
|
|
|
|
|
- uri-escape is compatible with uri-decode (load command) |
|
1856
|
|
|
|
|
|
|
set enter-whitespace on|off - allow \r \n and \t in SQL statements |
|
1857
|
|
|
|
|
|
|
set uri-encode on|off - allow all non ascii characters to be escaped |
|
1858
|
|
|
|
|
|
|
set auto-commit on|off - commit after every statement (default is OFF) |
|
1859
|
|
|
|
|
|
|
set longtruncok on|off - See DBI/LongTruncOk (default is on) |
|
1860
|
|
|
|
|
|
|
set longreadlen - See DBI/LongReadLen (default is 512) |
|
1861
|
|
|
|
|
|
|
set multiline on|off - multiline statements ending in ; (default is off) |
|
1862
|
|
|
|
|
|
|
set tracing on|off|deep - debug sqlsh using Log::Trace (default is off) |
|
1863
|
|
|
|
|
|
|
|
|
1864
|
|
|
|
|
|
|
log (queries|commands|all) - start logging to |
|
1865
|
|
|
|
|
|
|
no log - stop logging |
|
1866
|
|
|
|
|
|
|
|
|
1867
|
|
|
|
|
|
|
select ... |
|
1868
|
|
|
|
|
|
|
insert ... |
|
1869
|
|
|
|
|
|
|
update ... |
|
1870
|
|
|
|
|
|
|
create ... |
|
1871
|
|
|
|
|
|
|
alter ... |
|
1872
|
|
|
|
|
|
|
drop ... |
|
1873
|
|
|
|
|
|
|
grant ... |
|
1874
|
|
|
|
|
|
|
revoke ... |
|
1875
|
|
|
|
|
|
|
begin_work |
|
1876
|
|
|
|
|
|
|
commit |
|
1877
|
|
|
|
|
|
|
rollback |
|
1878
|
|
|
|
|
|
|
send ... |
|
1879
|
|
|
|
|
|
|
recv ... |
|
1880
|
|
|
|
|
|
|
|
|
1881
|
|
|
|
|
|
|
load into (delimited by foo) (uri-decode) (from bar to baz)
|
1882
|
|
|
|
|
|
|
- load delimited data from a file |
|
1883
|
|
|
|
|
|
|
- use uri-decode if file includes uri-encoded data |
|
1884
|
|
|
|
|
|
|
- from, to can take character set to recode data e.g. from CP1252 to UTF-8 |
|
1885
|
|
|
|
|
|
|
show charsets - display available character sets |
|
1886
|
|
|
|
|
|
|
dump into (delimited by foo) - dump delimited data
|
1887
|
|
|
|
|
|
|
dump into (delimited by foo) - dump delimited data |
|
1888
|
|
|
|
|
|
|
dump all tables into (delimited by foo) - dump delimited data |
|
1889
|
|
|
|
|
|
|
wipe tables - remove all data from DB (leaving tables empty) |
|
1890
|
|
|
|
|
|
|
|
|
1891
|
|
|
|
|
|
|
show history - display command history |
|
1892
|
|
|
|
|
|
|
clear history - erases the command history |
|
1893
|
|
|
|
|
|
|
save history to - saves the command history |
|
1894
|
|
|
|
|
|
|
load history from - loads the command history |
|
1895
|
|
|
|
|
|
|
execute - run a set of SQL or sqlsh commands from a file |
|
1896
|
|
|
|
|
|
|
|
|
1897
|
|
|
|
|
|
|
=head1 VERSION |
|
1898
|
|
|
|
|
|
|
|
|
1899
|
|
|
|
|
|
|
Version 1.17 |
|
1900
|
|
|
|
|
|
|
|
|
1901
|
|
|
|
|
|
|
=head1 AUTHOR |
|
1902
|
|
|
|
|
|
|
|
|
1903
|
|
|
|
|
|
|
John Alden with contributions by Simon Flack and Simon Stevenson |
|
1904
|
|
|
|
|
|
|
|
|
1905
|
|
|
|
|
|
|
Miguel Gualdron maintainer. |
|
1906
|
|
|
|
|
|
|
|
|
1907
|
|
|
|
|
|
|
=head1 COPYRIGHT |
|
1908
|
|
|
|
|
|
|
|
|
1909
|
|
|
|
|
|
|
SQL-Shell: Interactive shell for DBI Databases |
|
1910
|
|
|
|
|
|
|
Copyright (C) 2006 BBC |
|
1911
|
|
|
|
|
|
|
Copyright (C) 2019 Miguel Gualdron |
|
1912
|
|
|
|
|
|
|
|
|
1913
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify |
|
1914
|
|
|
|
|
|
|
it under the terms of the GNU General Public License as published by |
|
1915
|
|
|
|
|
|
|
the Free Software Foundation; either version 2 of the License, or |
|
1916
|
|
|
|
|
|
|
(at your option) any later version. |
|
1917
|
|
|
|
|
|
|
|
|
1918
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, |
|
1919
|
|
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
1920
|
|
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
1921
|
|
|
|
|
|
|
GNU General Public License for more details. |
|
1922
|
|
|
|
|
|
|
|
|
1923
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License |
|
1924
|
|
|
|
|
|
|
along with this program; if not, write to the Free Software |
|
1925
|
|
|
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
1926
|
|
|
|
|
|
|
|
|
1927
|
|
|
|
|
|
|
See the file COPYING in this distribution, or https://www.gnu.org/licenses/gpl-2.0.html |
|
1928
|
|
|
|
|
|
|
|
|
1929
|
|
|
|
|
|
|
=cut |
| | |