line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#line 1 |
2
|
|
|
|
|
|
|
# $Id: DBI.pm 14568 2010-12-14 15:23:58Z mjevans $ |
3
|
|
|
|
|
|
|
# vim: ts=8:sw=4:et |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# Copyright (c) 1994-2010 Tim Bunce Ireland |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
# See COPYRIGHT section in pod text below for usage and distribution rights. |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
require 5.008_001; |
11
|
|
|
|
|
|
|
|
12
|
2
|
|
|
2
|
|
58
|
BEGIN { |
13
|
|
|
|
|
|
|
$DBI::VERSION = "1.616"; # ==> ALSO update the version in the pod text below! |
14
|
|
|
|
|
|
|
} |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
#line 152 |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# The POD text continues at the end of the file. |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
package DBI; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
use Carp(); |
24
|
|
|
|
|
|
|
use DynaLoader (); |
25
|
|
|
|
|
|
|
use Exporter (); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
BEGIN { |
28
|
|
|
|
|
|
|
@ISA = qw(Exporter DynaLoader); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# Make some utility functions available if asked for |
31
|
|
|
|
|
|
|
@EXPORT = (); # we export nothing by default |
32
|
|
|
|
|
|
|
@EXPORT_OK = qw(%DBI %DBI_methods hash); # also populated by export_ok_tags: |
33
|
|
|
|
|
|
|
%EXPORT_TAGS = ( |
34
|
|
|
|
|
|
|
sql_types => [ qw( |
35
|
|
|
|
|
|
|
SQL_GUID |
36
|
|
|
|
|
|
|
SQL_WLONGVARCHAR |
37
|
|
|
|
|
|
|
SQL_WVARCHAR |
38
|
|
|
|
|
|
|
SQL_WCHAR |
39
|
|
|
|
|
|
|
SQL_BIGINT |
40
|
|
|
|
|
|
|
SQL_BIT |
41
|
|
|
|
|
|
|
SQL_TINYINT |
42
|
|
|
|
|
|
|
SQL_LONGVARBINARY |
43
|
|
|
|
|
|
|
SQL_VARBINARY |
44
|
|
|
|
|
|
|
SQL_BINARY |
45
|
|
|
|
|
|
|
SQL_LONGVARCHAR |
46
|
|
|
|
|
|
|
SQL_UNKNOWN_TYPE |
47
|
|
|
|
|
|
|
SQL_ALL_TYPES |
48
|
|
|
|
|
|
|
SQL_CHAR |
49
|
|
|
|
|
|
|
SQL_NUMERIC |
50
|
|
|
|
|
|
|
SQL_DECIMAL |
51
|
|
|
|
|
|
|
SQL_INTEGER |
52
|
|
|
|
|
|
|
SQL_SMALLINT |
53
|
|
|
|
|
|
|
SQL_FLOAT |
54
|
|
|
|
|
|
|
SQL_REAL |
55
|
|
|
|
|
|
|
SQL_DOUBLE |
56
|
|
|
|
|
|
|
SQL_DATETIME |
57
|
|
|
|
|
|
|
SQL_DATE |
58
|
|
|
|
|
|
|
SQL_INTERVAL |
59
|
|
|
|
|
|
|
SQL_TIME |
60
|
|
|
|
|
|
|
SQL_TIMESTAMP |
61
|
|
|
|
|
|
|
SQL_VARCHAR |
62
|
|
|
|
|
|
|
SQL_BOOLEAN |
63
|
|
|
|
|
|
|
SQL_UDT |
64
|
|
|
|
|
|
|
SQL_UDT_LOCATOR |
65
|
|
|
|
|
|
|
SQL_ROW |
66
|
|
|
|
|
|
|
SQL_REF |
67
|
|
|
|
|
|
|
SQL_BLOB |
68
|
|
|
|
|
|
|
SQL_BLOB_LOCATOR |
69
|
|
|
|
|
|
|
SQL_CLOB |
70
|
|
|
|
|
|
|
SQL_CLOB_LOCATOR |
71
|
|
|
|
|
|
|
SQL_ARRAY |
72
|
|
|
|
|
|
|
SQL_ARRAY_LOCATOR |
73
|
|
|
|
|
|
|
SQL_MULTISET |
74
|
|
|
|
|
|
|
SQL_MULTISET_LOCATOR |
75
|
|
|
|
|
|
|
SQL_TYPE_DATE |
76
|
|
|
|
|
|
|
SQL_TYPE_TIME |
77
|
|
|
|
|
|
|
SQL_TYPE_TIMESTAMP |
78
|
|
|
|
|
|
|
SQL_TYPE_TIME_WITH_TIMEZONE |
79
|
|
|
|
|
|
|
SQL_TYPE_TIMESTAMP_WITH_TIMEZONE |
80
|
|
|
|
|
|
|
SQL_INTERVAL_YEAR |
81
|
|
|
|
|
|
|
SQL_INTERVAL_MONTH |
82
|
|
|
|
|
|
|
SQL_INTERVAL_DAY |
83
|
|
|
|
|
|
|
SQL_INTERVAL_HOUR |
84
|
|
|
|
|
|
|
SQL_INTERVAL_MINUTE |
85
|
|
|
|
|
|
|
SQL_INTERVAL_SECOND |
86
|
|
|
|
|
|
|
SQL_INTERVAL_YEAR_TO_MONTH |
87
|
|
|
|
|
|
|
SQL_INTERVAL_DAY_TO_HOUR |
88
|
|
|
|
|
|
|
SQL_INTERVAL_DAY_TO_MINUTE |
89
|
|
|
|
|
|
|
SQL_INTERVAL_DAY_TO_SECOND |
90
|
|
|
|
|
|
|
SQL_INTERVAL_HOUR_TO_MINUTE |
91
|
|
|
|
|
|
|
SQL_INTERVAL_HOUR_TO_SECOND |
92
|
|
|
|
|
|
|
SQL_INTERVAL_MINUTE_TO_SECOND |
93
|
|
|
|
|
|
|
DBIstcf_DISCARD_STRING |
94
|
|
|
|
|
|
|
DBIstcf_STRICT |
95
|
|
|
|
|
|
|
) ], |
96
|
|
|
|
|
|
|
sql_cursor_types => [ qw( |
97
|
|
|
|
|
|
|
SQL_CURSOR_FORWARD_ONLY |
98
|
|
|
|
|
|
|
SQL_CURSOR_KEYSET_DRIVEN |
99
|
|
|
|
|
|
|
SQL_CURSOR_DYNAMIC |
100
|
|
|
|
|
|
|
SQL_CURSOR_STATIC |
101
|
|
|
|
|
|
|
SQL_CURSOR_TYPE_DEFAULT |
102
|
|
|
|
|
|
|
) ], # for ODBC cursor types |
103
|
|
|
|
|
|
|
utils => [ qw( |
104
|
|
|
|
|
|
|
neat neat_list $neat_maxlen dump_results looks_like_number |
105
|
|
|
|
|
|
|
data_string_diff data_string_desc data_diff sql_type_cast |
106
|
|
|
|
|
|
|
) ], |
107
|
|
|
|
|
|
|
profile => [ qw( |
108
|
|
|
|
|
|
|
dbi_profile dbi_profile_merge dbi_profile_merge_nodes dbi_time |
109
|
|
|
|
|
|
|
) ], # notionally "in" DBI::Profile and normally imported from there |
110
|
|
|
|
|
|
|
); |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
$DBI::dbi_debug = 0; |
113
|
|
|
|
|
|
|
$DBI::neat_maxlen = 1000; |
114
|
|
|
|
|
|
|
$DBI::stderr = 2_000_000_000; # a very round number below 2**31 |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# If you get an error here like "Can't find loadable object ..." |
117
|
|
|
|
|
|
|
# then you haven't installed the DBI correctly. Read the README |
118
|
|
|
|
|
|
|
# then install it again. |
119
|
|
|
|
|
|
|
if ( $ENV{DBI_PUREPERL} ) { |
120
|
|
|
|
|
|
|
eval { bootstrap DBI } if $ENV{DBI_PUREPERL} == 1; |
121
|
|
|
|
|
|
|
require DBI::PurePerl if $@ or $ENV{DBI_PUREPERL} >= 2; |
122
|
|
|
|
|
|
|
$DBI::PurePerl ||= 0; # just to silence "only used once" warnings |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
else { |
125
|
|
|
|
|
|
|
bootstrap DBI; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
$EXPORT_TAGS{preparse_flags} = [ grep { /^DBIpp_\w\w_/ } keys %{__PACKAGE__."::"} ]; |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
Exporter::export_ok_tags(keys %EXPORT_TAGS); |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# Alias some handle methods to also be DBI class methods |
135
|
|
|
|
|
|
|
for (qw(trace_msg set_err parse_trace_flag parse_trace_flags)) { |
136
|
|
|
|
|
|
|
no strict; |
137
|
|
|
|
|
|
|
*$_ = \&{"DBD::_::common::$_"}; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
use strict; |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
DBI->trace(split /=/, $ENV{DBI_TRACE}, 2) if $ENV{DBI_TRACE}; |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
$DBI::connect_via ||= "connect"; |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# check if user wants a persistent database connection ( Apache + mod_perl ) |
147
|
|
|
|
|
|
|
if ($INC{'Apache/DBI.pm'} && $ENV{MOD_PERL}) { |
148
|
|
|
|
|
|
|
$DBI::connect_via = "Apache::DBI::connect"; |
149
|
|
|
|
|
|
|
DBI->trace_msg("DBI connect via $DBI::connect_via in $INC{'Apache/DBI.pm'}\n"); |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# check for weaken support, used by ChildHandles |
153
|
|
|
|
|
|
|
my $HAS_WEAKEN = eval { |
154
|
|
|
|
|
|
|
require Scalar::Util; |
155
|
|
|
|
|
|
|
# this will croak() if this Scalar::Util doesn't have a working weaken(). |
156
|
|
|
|
|
|
|
Scalar::Util::weaken( \my $test ); # same test as in t/72childhandles.t |
157
|
|
|
|
|
|
|
1; |
158
|
2
|
|
|
2
|
|
10
|
}; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
28
|
|
159
|
2
|
|
|
2
|
|
9
|
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
38
|
|
160
|
2
|
|
|
2
|
|
15
|
%DBI::installed_drh = (); # maps driver names to installed driver handles |
|
2
|
|
|
|
|
9
|
|
|
2
|
|
|
|
|
718
|
|
161
|
|
|
|
|
|
|
sub installed_drivers { %DBI::installed_drh } |
162
|
|
|
|
|
|
|
%DBI::installed_methods = (); # XXX undocumented, may change |
163
|
2
|
|
|
2
|
|
38
|
sub installed_methods { %DBI::installed_methods } |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# Setup special DBI dynamic variables. See DBI::var::FETCH for details. |
166
|
2
|
|
|
|
|
4
|
# These are dynamically associated with the last handle used. |
167
|
2
|
|
|
|
|
6
|
tie $DBI::err, 'DBI::var', '*err'; # special case: referenced via IHA list |
168
|
2
|
|
|
|
|
66
|
tie $DBI::state, 'DBI::var', '"state'; # special case: referenced via IHA list |
169
|
|
|
|
|
|
|
tie $DBI::lasth, 'DBI::var', '!lasth'; # special case: return boolean |
170
|
|
|
|
|
|
|
tie $DBI::errstr, 'DBI::var', '&errstr'; # call &errstr in last used pkg |
171
|
|
|
|
|
|
|
tie $DBI::rows, 'DBI::var', '&rows'; # call &rows in last used pkg |
172
|
|
|
|
|
|
|
sub DBI::var::TIESCALAR{ my $var = $_[1]; bless \$var, 'DBI::var'; } |
173
|
|
|
|
|
|
|
sub DBI::var::STORE { Carp::croak("Can't modify \$DBI::${$_[0]} special variable") } |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
{ # used to catch DBI->{Attrib} mistake |
176
|
|
|
|
|
|
|
sub DBI::DBI_tie::TIEHASH { bless {} } |
177
|
|
|
|
|
|
|
sub DBI::DBI_tie::STORE { Carp::carp("DBI->{$_[1]} is invalid syntax (you probably want \$h->{$_[1]})");} |
178
|
|
|
|
|
|
|
*DBI::DBI_tie::FETCH = \&DBI::DBI_tie::STORE; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
tie %DBI::DBI => 'DBI::DBI_tie'; |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# --- Driver Specific Prefix Registry --- |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
my $dbd_prefix_registry = { |
185
|
|
|
|
|
|
|
ad_ => { class => 'DBD::AnyData', }, |
186
|
|
|
|
|
|
|
ado_ => { class => 'DBD::ADO', }, |
187
|
|
|
|
|
|
|
amzn_ => { class => 'DBD::Amazon', }, |
188
|
|
|
|
|
|
|
best_ => { class => 'DBD::BestWins', }, |
189
|
|
|
|
|
|
|
csv_ => { class => 'DBD::CSV', }, |
190
|
|
|
|
|
|
|
db2_ => { class => 'DBD::DB2', }, |
191
|
|
|
|
|
|
|
dbi_ => { class => 'DBI', }, |
192
|
|
|
|
|
|
|
dbm_ => { class => 'DBD::DBM', }, |
193
|
|
|
|
|
|
|
df_ => { class => 'DBD::DF', }, |
194
|
|
|
|
|
|
|
f_ => { class => 'DBD::File', }, |
195
|
|
|
|
|
|
|
file_ => { class => 'DBD::TextFile', }, |
196
|
|
|
|
|
|
|
go_ => { class => 'DBD::Gofer', }, |
197
|
|
|
|
|
|
|
ib_ => { class => 'DBD::InterBase', }, |
198
|
|
|
|
|
|
|
ing_ => { class => 'DBD::Ingres', }, |
199
|
|
|
|
|
|
|
ix_ => { class => 'DBD::Informix', }, |
200
|
|
|
|
|
|
|
jdbc_ => { class => 'DBD::JDBC', }, |
201
|
|
|
|
|
|
|
monetdb_ => { class => 'DBD::monetdb', }, |
202
|
|
|
|
|
|
|
msql_ => { class => 'DBD::mSQL', }, |
203
|
|
|
|
|
|
|
mvsftp_ => { class => 'DBD::MVS_FTPSQL', }, |
204
|
|
|
|
|
|
|
mysql_ => { class => 'DBD::mysql', }, |
205
|
|
|
|
|
|
|
mx_ => { class => 'DBD::Multiplex', }, |
206
|
|
|
|
|
|
|
nullp_ => { class => 'DBD::NullP', }, |
207
|
|
|
|
|
|
|
odbc_ => { class => 'DBD::ODBC', }, |
208
|
|
|
|
|
|
|
ora_ => { class => 'DBD::Oracle', }, |
209
|
|
|
|
|
|
|
pg_ => { class => 'DBD::Pg', }, |
210
|
|
|
|
|
|
|
pgpp_ => { class => 'DBD::PgPP', }, |
211
|
|
|
|
|
|
|
plb_ => { class => 'DBD::Plibdata', }, |
212
|
|
|
|
|
|
|
po_ => { class => 'DBD::PO', }, |
213
|
|
|
|
|
|
|
proxy_ => { class => 'DBD::Proxy', }, |
214
|
|
|
|
|
|
|
ram_ => { class => 'DBD::RAM', }, |
215
|
|
|
|
|
|
|
rdb_ => { class => 'DBD::RDB', }, |
216
|
|
|
|
|
|
|
sapdb_ => { class => 'DBD::SAP_DB', }, |
217
|
|
|
|
|
|
|
solid_ => { class => 'DBD::Solid', }, |
218
|
|
|
|
|
|
|
sponge_ => { class => 'DBD::Sponge', }, |
219
|
|
|
|
|
|
|
sql_ => { class => 'DBI::DBD::SqlEngine', }, |
220
|
|
|
|
|
|
|
sqlite_ => { class => 'DBD::SQLite', }, |
221
|
|
|
|
|
|
|
syb_ => { class => 'DBD::Sybase', }, |
222
|
|
|
|
|
|
|
sys_ => { class => 'DBD::Sys', }, |
223
|
|
|
|
|
|
|
tdat_ => { class => 'DBD::Teradata', }, |
224
|
|
|
|
|
|
|
tmpl_ => { class => 'DBD::Template', }, |
225
|
|
|
|
|
|
|
tmplss_ => { class => 'DBD::TemplateSS', }, |
226
|
|
|
|
|
|
|
tuber_ => { class => 'DBD::Tuber', }, |
227
|
|
|
|
|
|
|
uni_ => { class => 'DBD::Unify', }, |
228
|
|
|
|
|
|
|
vt_ => { class => 'DBD::Vt', }, |
229
|
|
|
|
|
|
|
wmi_ => { class => 'DBD::WMI', }, |
230
|
|
|
|
|
|
|
x_ => { }, # for private use |
231
|
|
|
|
|
|
|
xbase_ => { class => 'DBD::XBase', }, |
232
|
|
|
|
|
|
|
xl_ => { class => 'DBD::Excel', }, |
233
|
|
|
|
|
|
|
yaswi_ => { class => 'DBD::Yaswi', }, |
234
|
|
|
|
|
|
|
}; |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
my %dbd_class_registry = map { $dbd_prefix_registry->{$_}->{class} => { prefix => $_ } } |
237
|
|
|
|
|
|
|
grep { exists $dbd_prefix_registry->{$_}->{class} } |
238
|
|
|
|
|
|
|
keys %{$dbd_prefix_registry}; |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
sub dump_dbd_registry { |
241
|
|
|
|
|
|
|
require Data::Dumper; |
242
|
|
|
|
|
|
|
local $Data::Dumper::Sortkeys=1; |
243
|
|
|
|
|
|
|
local $Data::Dumper::Indent=1; |
244
|
|
|
|
|
|
|
print Data::Dumper->Dump([$dbd_prefix_registry], [qw($dbd_prefix_registry)]); |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
2
|
|
|
|
|
4
|
# --- Dynamically create the DBI Standard Interface |
248
|
2
|
|
|
|
|
2
|
|
249
|
2
|
|
|
|
|
4
|
my $keeperr = { O=>0x0004 }; |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
%DBI::DBI_methods = ( # Define the DBI interface methods per class: |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
common => { # Interface methods common to all DBI handle classes |
254
|
2
|
50
|
|
|
|
9
|
'DESTROY' => { O=>0x004|0x10000 }, |
255
|
0
|
0
|
|
|
|
0
|
'CLEAR' => $keeperr, |
|
0
|
|
|
|
|
0
|
|
256
|
0
|
0
|
0
|
|
|
0
|
'EXISTS' => $keeperr, |
257
|
0
|
|
0
|
|
|
0
|
'FETCH' => { O=>0x0404 }, |
258
|
|
|
|
|
|
|
'FETCH_many' => { O=>0x0404 }, |
259
|
|
|
|
|
|
|
'FIRSTKEY' => $keeperr, |
260
|
2
|
|
|
|
|
1568
|
'NEXTKEY' => $keeperr, |
261
|
|
|
|
|
|
|
'STORE' => { O=>0x0418 | 0x4 }, |
262
|
|
|
|
|
|
|
_not_impl => undef, |
263
|
0
|
|
|
|
|
|
can => { O=>0x0100 }, # special case, see dispatch |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
debug => { U =>[1,2,'[$debug_level]'], O=>0x0004 }, # old name for trace |
265
|
0
|
|
|
|
|
|
dump_handle => { U =>[1,3,'[$message [, $level]]'], O=>0x0004 }, |
266
|
|
|
|
|
|
|
err => $keeperr, |
267
|
|
|
|
|
|
|
errstr => $keeperr, |
268
|
|
|
|
|
|
|
state => $keeperr, |
269
|
|
|
|
|
|
|
func => { O=>0x0006 }, |
270
|
|
|
|
|
|
|
parse_trace_flag => { U =>[2,2,'$name'], O=>0x0404, T=>8 }, |
271
|
|
|
|
|
|
|
parse_trace_flags => { U =>[2,2,'$flags'], O=>0x0404, T=>8 }, |
272
|
|
|
|
|
|
|
private_data => { U =>[1,1], O=>0x0004 }, |
273
|
|
|
|
|
|
|
set_err => { U =>[3,6,'$err, $errmsg [, $state, $method, $rv]'], O=>0x0010 }, |
274
|
|
|
|
|
|
|
trace => { U =>[1,3,'[$trace_level, [$filename]]'], O=>0x0004 }, |
275
|
|
|
|
|
|
|
trace_msg => { U =>[2,3,'$message_text [, $min_level ]' ], O=>0x0004, T=>8 }, |
276
|
|
|
|
|
|
|
swap_inner_handle => { U =>[2,3,'$h [, $allow_reparent ]'] }, |
277
|
|
|
|
|
|
|
private_attribute_info => { }, |
278
|
|
|
|
|
|
|
visit_child_handles => { U => [2,3,'$coderef [, $info ]'], O=>0x0404, T=>4 }, |
279
|
|
|
|
|
|
|
}, |
280
|
|
|
|
|
|
|
dr => { # Database Driver Interface |
281
|
|
|
|
|
|
|
'connect' => { U =>[1,5,'[$db [,$user [,$passwd [,\%attr]]]]'], H=>3, O=>0x8000 }, |
282
|
|
|
|
|
|
|
'connect_cached'=>{U=>[1,5,'[$db [,$user [,$passwd [,\%attr]]]]'], H=>3, O=>0x8000 }, |
283
|
|
|
|
|
|
|
'disconnect_all'=>{ U =>[1,1], O=>0x0800 }, |
284
|
|
|
|
|
|
|
data_sources => { U =>[1,2,'[\%attr]' ], O=>0x0800 }, |
285
|
|
|
|
|
|
|
default_user => { U =>[3,4,'$user, $pass [, \%attr]' ] }, |
286
|
|
|
|
|
|
|
dbixs_revision => $keeperr, |
287
|
|
|
|
|
|
|
}, |
288
|
|
|
|
|
|
|
db => { # Database Session Class Interface |
289
|
|
|
|
|
|
|
data_sources => { U =>[1,2,'[\%attr]' ], O=>0x0200 }, |
290
|
|
|
|
|
|
|
take_imp_data => { U =>[1,1], O=>0x10000 }, |
291
|
|
|
|
|
|
|
clone => { U =>[1,2,'[\%attr]'] }, |
292
|
|
|
|
|
|
|
connected => { U =>[1,0], O => 0x0004 }, |
293
|
|
|
|
|
|
|
begin_work => { U =>[1,2,'[ \%attr ]'], O=>0x0400 }, |
294
|
|
|
|
|
|
|
commit => { U =>[1,1], O=>0x0480|0x0800 }, |
295
|
|
|
|
|
|
|
rollback => { U =>[1,1], O=>0x0480|0x0800 }, |
296
|
|
|
|
|
|
|
'do' => { U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'], O=>0x3200 }, |
297
|
|
|
|
|
|
|
last_insert_id => { U =>[5,6,'$catalog, $schema, $table_name, $field_name [, \%attr ]'], O=>0x2800 }, |
298
|
|
|
|
|
|
|
preparse => { }, # XXX |
299
|
|
|
|
|
|
|
prepare => { U =>[2,3,'$statement [, \%attr]'], O=>0xA200 }, |
300
|
|
|
|
|
|
|
prepare_cached => { U =>[2,4,'$statement [, \%attr [, $if_active ] ]'], O=>0xA200 }, |
301
|
|
|
|
|
|
|
selectrow_array => { U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'], O=>0x2000 }, |
302
|
|
|
|
|
|
|
selectrow_arrayref=>{U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'], O=>0x2000 }, |
303
|
|
|
|
|
|
|
selectrow_hashref=>{ U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'], O=>0x2000 }, |
304
|
|
|
|
|
|
|
selectall_arrayref=>{U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'], O=>0x2000 }, |
305
|
|
|
|
|
|
|
selectall_hashref=>{ U =>[3,0,'$statement, $keyfield [, \%attr [, @bind_params ] ]'], O=>0x2000 }, |
306
|
|
|
|
|
|
|
selectcol_arrayref=>{U =>[2,0,'$statement [, \%attr [, @bind_params ] ]'], O=>0x2000 }, |
307
|
|
|
|
|
|
|
ping => { U =>[1,1], O=>0x0404 }, |
308
|
|
|
|
|
|
|
disconnect => { U =>[1,1], O=>0x0400|0x0800|0x10000 }, |
309
|
|
|
|
|
|
|
quote => { U =>[2,3, '$string [, $data_type ]' ], O=>0x0430 }, |
310
|
|
|
|
|
|
|
quote_identifier=> { U =>[2,6, '$name [, ...] [, \%attr ]' ], O=>0x0430 }, |
311
|
|
|
|
|
|
|
rows => $keeperr, |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
tables => { U =>[1,6,'$catalog, $schema, $table, $type [, \%attr ]' ], O=>0x2200 }, |
314
|
|
|
|
|
|
|
table_info => { U =>[1,6,'$catalog, $schema, $table, $type [, \%attr ]' ], O=>0x2200|0x8800 }, |
315
|
|
|
|
|
|
|
column_info => { U =>[5,6,'$catalog, $schema, $table, $column [, \%attr ]'],O=>0x2200|0x8800 }, |
316
|
|
|
|
|
|
|
primary_key_info=> { U =>[4,5,'$catalog, $schema, $table [, \%attr ]' ], O=>0x2200|0x8800 }, |
317
|
|
|
|
|
|
|
primary_key => { U =>[4,5,'$catalog, $schema, $table [, \%attr ]' ], O=>0x2200 }, |
318
|
|
|
|
|
|
|
foreign_key_info=> { U =>[7,8,'$pk_catalog, $pk_schema, $pk_table, $fk_catalog, $fk_schema, $fk_table [, \%attr ]' ], O=>0x2200|0x8800 }, |
319
|
|
|
|
|
|
|
statistics_info => { U =>[6,7,'$catalog, $schema, $table, $unique_only, $quick, [, \%attr ]' ], O=>0x2200|0x8800 }, |
320
|
|
|
|
|
|
|
type_info_all => { U =>[1,1], O=>0x2200|0x0800 }, |
321
|
|
|
|
|
|
|
type_info => { U =>[1,2,'$data_type'], O=>0x2200 }, |
322
|
|
|
|
|
|
|
get_info => { U =>[2,2,'$info_type'], O=>0x2200|0x0800 }, |
323
|
|
|
|
|
|
|
}, |
324
|
|
|
|
|
|
|
st => { # Statement Class Interface |
325
|
|
|
|
|
|
|
bind_col => { U =>[3,4,'$column, \\$var [, \%attr]'] }, |
326
|
|
|
|
|
|
|
bind_columns => { U =>[2,0,'\\$var1 [, \\$var2, ...]'] }, |
327
|
|
|
|
|
|
|
bind_param => { U =>[3,4,'$parameter, $var [, \%attr]'] }, |
328
|
|
|
|
|
|
|
bind_param_inout=> { U =>[4,5,'$parameter, \\$var, $maxlen, [, \%attr]'] }, |
329
|
|
|
|
|
|
|
execute => { U =>[1,0,'[@args]'], O=>0x1040 }, |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
bind_param_array => { U =>[3,4,'$parameter, $var [, \%attr]'] }, |
332
|
|
|
|
|
|
|
bind_param_inout_array => { U =>[4,5,'$parameter, \\@var, $maxlen, [, \%attr]'] }, |
333
|
|
|
|
|
|
|
execute_array => { U =>[2,0,'\\%attribs [, @args]'], O=>0x1040|0x4000 }, |
334
|
|
|
|
|
|
|
execute_for_fetch => { U =>[2,3,'$fetch_sub [, $tuple_status]'], O=>0x1040|0x4000 }, |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
fetch => undef, # alias for fetchrow_arrayref |
337
|
|
|
|
|
|
|
fetchrow_arrayref => undef, |
338
|
|
|
|
|
|
|
fetchrow_hashref => undef, |
339
|
|
|
|
|
|
|
fetchrow_array => undef, |
340
|
|
|
|
|
|
|
fetchrow => undef, # old alias for fetchrow_array |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
fetchall_arrayref => { U =>[1,3, '[ $slice [, $max_rows]]'] }, |
343
|
|
|
|
|
|
|
fetchall_hashref => { U =>[2,2,'$key_field'] }, |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
blob_read => { U =>[4,5,'$field, $offset, $len [, \\$buf [, $bufoffset]]'] }, |
346
|
|
|
|
|
|
|
blob_copy_to_file => { U =>[3,3,'$field, $filename_or_handleref'] }, |
347
|
|
|
|
|
|
|
dump_results => { U =>[1,5,'$maxfieldlen, $linesep, $fieldsep, $filehandle'] }, |
348
|
|
|
|
|
|
|
more_results => { U =>[1,1] }, |
349
|
|
|
|
|
|
|
finish => { U =>[1,1] }, |
350
|
|
|
|
|
|
|
cancel => { U =>[1,1], O=>0x0800 }, |
351
|
|
|
|
|
|
|
rows => $keeperr, |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
_get_fbav => undef, |
354
|
|
|
|
|
|
|
_set_fbav => { T=>6 }, |
355
|
|
|
|
|
|
|
}, |
356
|
|
|
|
|
|
|
); |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
while ( my ($class, $meths) = each %DBI::DBI_methods ) { |
359
|
|
|
|
|
|
|
my $ima_trace = 0+($ENV{DBI_IMA_TRACE}||0); |
360
|
|
|
|
|
|
|
while ( my ($method, $info) = each %$meths ) { |
361
|
|
|
|
|
|
|
my $fullmeth = "DBI::${class}::$method"; |
362
|
|
|
|
|
|
|
if ($DBI::dbi_debug >= 15) { # quick hack to list DBI methods |
363
|
|
|
|
|
|
|
# and optionally filter by IMA flags |
364
|
|
|
|
|
|
|
my $O = $info->{O}||0; |
365
|
|
|
|
|
|
|
printf "0x%04x %-20s\n", $O, $fullmeth |
366
|
|
|
|
|
|
|
unless $ima_trace && !($O & $ima_trace); |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
DBI->_install_method($fullmeth, 'DBI.pm', $info); |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
{ |
373
|
|
|
|
|
|
|
package DBI::common; |
374
|
|
|
|
|
|
|
@DBI::dr::ISA = ('DBI::common'); |
375
|
|
|
|
|
|
|
@DBI::db::ISA = ('DBI::common'); |
376
|
|
|
|
|
|
|
@DBI::st::ISA = ('DBI::common'); |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
# End of init code |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
END { |
383
|
|
|
|
|
|
|
return unless defined &DBI::trace_msg; # return unless bootstrap'd ok |
384
|
|
|
|
|
|
|
local ($!,$?); |
385
|
|
|
|
|
|
|
DBI->trace_msg(sprintf(" -- DBI::END (\$\@: %s, \$!: %s)\n", $@||'', $!||''), 2); |
386
|
|
|
|
|
|
|
# Let drivers know why we are calling disconnect_all: |
387
|
|
|
|
|
|
|
$DBI::PERL_ENDING = $DBI::PERL_ENDING = 1; # avoid typo warning |
388
|
|
|
|
|
|
|
DBI->disconnect_all() if %DBI::installed_drh; |
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
sub CLONE { |
393
|
|
|
|
|
|
|
my $olddbis = $DBI::_dbistate; |
394
|
|
|
|
|
|
|
_clone_dbis() unless $DBI::PurePerl; # clone the DBIS structure |
395
|
|
|
|
|
|
|
DBI->trace_msg(sprintf "CLONE DBI for new thread %s\n", |
396
|
|
|
|
|
|
|
$DBI::PurePerl ? "" : sprintf("(dbis %x -> %x)",$olddbis, $DBI::_dbistate)); |
397
|
|
|
|
|
|
|
while ( my ($driver, $drh) = each %DBI::installed_drh) { |
398
|
|
|
|
|
|
|
no strict 'refs'; |
399
|
|
|
|
|
|
|
next if defined &{"DBD::${driver}::CLONE"}; |
400
|
|
|
|
|
|
|
warn("$driver has no driver CLONE() function so is unsafe threaded\n"); |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
%DBI::installed_drh = (); # clear loaded drivers so they have a chance to reinitialize |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
sub parse_dsn { |
406
|
|
|
|
|
|
|
my ($class, $dsn) = @_; |
407
|
|
|
|
|
|
|
$dsn =~ s/^(dbi):(\w*?)(?:\((.*?)\))?://i or return; |
408
|
|
|
|
|
|
|
my ($scheme, $driver, $attr, $attr_hash) = (lc($1), $2, $3); |
409
|
|
|
|
|
|
|
$driver ||= $ENV{DBI_DRIVER} || ''; |
410
|
|
|
|
|
|
|
$attr_hash = { split /\s*=>?\s*|\s*,\s*/, $attr, -1 } if $attr; |
411
|
|
|
|
|
|
|
return ($scheme, $driver, $attr, $attr_hash, $dsn); |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
sub visit_handles { |
415
|
|
|
|
|
|
|
my ($class, $code, $outer_info) = @_; |
416
|
|
|
|
|
|
|
$outer_info = {} if not defined $outer_info; |
417
|
|
|
|
|
|
|
my %drh = DBI->installed_drivers; |
418
|
|
|
|
|
|
|
for my $h (values %drh) { |
419
|
|
|
|
|
|
|
my $child_info = $code->($h, $outer_info) |
420
|
|
|
|
|
|
|
or next; |
421
|
|
|
|
|
|
|
$h->visit_child_handles($code, $child_info); |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
return $outer_info; |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
# --- The DBI->connect Front Door methods |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
sub connect_cached { |
430
|
|
|
|
|
|
|
# For library code using connect_cached() with mod_perl |
431
|
|
|
|
|
|
|
# we redirect those calls to Apache::DBI::connect() as well |
432
|
|
|
|
|
|
|
my ($class, $dsn, $user, $pass, $attr) = @_; |
433
|
|
|
|
|
|
|
my $dbi_connect_method = ($DBI::connect_via eq "Apache::DBI::connect") |
434
|
|
|
|
|
|
|
? 'Apache::DBI::connect' : 'connect_cached'; |
435
|
|
|
|
|
|
|
$attr = { |
436
|
|
|
|
|
|
|
$attr ? %$attr : (), # clone, don't modify callers data |
437
|
|
|
|
|
|
|
dbi_connect_method => $dbi_connect_method, |
438
|
|
|
|
|
|
|
}; |
439
|
|
|
|
|
|
|
return $class->connect($dsn, $user, $pass, $attr); |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
sub connect { |
443
|
|
|
|
|
|
|
my $class = shift; |
444
|
|
|
|
|
|
|
my ($dsn, $user, $pass, $attr, $old_driver) = my @orig_args = @_; |
445
|
|
|
|
|
|
|
my $driver; |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
if ($attr and !ref($attr)) { # switch $old_driver<->$attr if called in old style |
448
|
|
|
|
|
|
|
Carp::carp("DBI->connect using 'old-style' syntax is deprecated and will be an error in future versions"); |
449
|
|
|
|
|
|
|
($old_driver, $attr) = ($attr, $old_driver); |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
my $connect_meth = $attr->{dbi_connect_method}; |
453
|
|
|
|
|
|
|
$connect_meth ||= $DBI::connect_via; # fallback to default |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
$dsn ||= $ENV{DBI_DSN} || $ENV{DBI_DBNAME} || '' unless $old_driver; |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
if ($DBI::dbi_debug) { |
458
|
|
|
|
|
|
|
local $^W = 0; |
459
|
|
|
|
|
|
|
pop @_ if $connect_meth ne 'connect'; |
460
|
|
|
|
|
|
|
my @args = @_; $args[2] = '****'; # hide password |
461
|
|
|
|
|
|
|
DBI->trace_msg(" -> $class->$connect_meth(".join(", ",@args).")\n"); |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
Carp::croak('Usage: $class->connect([$dsn [,$user [,$passwd [,\%attr]]]])') |
464
|
|
|
|
|
|
|
if (ref $old_driver or ($attr and not ref $attr) or ref $pass); |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
# extract dbi:driver prefix from $dsn into $1 |
467
|
|
|
|
|
|
|
$dsn =~ s/^dbi:(\w*?)(?:\((.*?)\))?://i |
468
|
|
|
|
|
|
|
or '' =~ /()/; # ensure $1 etc are empty if match fails |
469
|
|
|
|
|
|
|
my $driver_attrib_spec = $2 || ''; |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
# Set $driver. Old style driver, if specified, overrides new dsn style. |
472
|
|
|
|
|
|
|
$driver = $old_driver || $1 || $ENV{DBI_DRIVER} |
473
|
|
|
|
|
|
|
or Carp::croak("Can't connect to data source '$dsn' " |
474
|
|
|
|
|
|
|
."because I can't work out what driver to use " |
475
|
|
|
|
|
|
|
."(it doesn't seem to contain a 'dbi:driver:' prefix " |
476
|
|
|
|
|
|
|
."and the DBI_DRIVER env var is not set)"); |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
my $proxy; |
479
|
|
|
|
|
|
|
if ($ENV{DBI_AUTOPROXY} && $driver ne 'Proxy' && $driver ne 'Sponge' && $driver ne 'Switch') { |
480
|
|
|
|
|
|
|
my $dbi_autoproxy = $ENV{DBI_AUTOPROXY}; |
481
|
|
|
|
|
|
|
$proxy = 'Proxy'; |
482
|
|
|
|
|
|
|
if ($dbi_autoproxy =~ s/^dbi:(\w*?)(?:\((.*?)\))?://i) { |
483
|
|
|
|
|
|
|
$proxy = $1; |
484
|
|
|
|
|
|
|
$driver_attrib_spec = join ",", |
485
|
|
|
|
|
|
|
($driver_attrib_spec) ? $driver_attrib_spec : (), |
486
|
|
|
|
|
|
|
($2 ) ? $2 : (); |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
$dsn = "$dbi_autoproxy;dsn=dbi:$driver:$dsn"; |
489
|
|
|
|
|
|
|
$driver = $proxy; |
490
|
|
|
|
|
|
|
DBI->trace_msg(" DBI_AUTOPROXY: dbi:$driver($driver_attrib_spec):$dsn\n"); |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
# avoid recursion if proxy calls DBI->connect itself |
493
|
|
|
|
|
|
|
local $ENV{DBI_AUTOPROXY} if $ENV{DBI_AUTOPROXY}; |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
my %attributes; # take a copy we can delete from |
496
|
|
|
|
|
|
|
if ($old_driver) { |
497
|
|
|
|
|
|
|
%attributes = %$attr if $attr; |
498
|
|
|
|
|
|
|
} |
499
|
|
|
|
|
|
|
else { # new-style connect so new default semantics |
500
|
|
|
|
|
|
|
%attributes = ( |
501
|
|
|
|
|
|
|
PrintError => 1, |
502
|
|
|
|
|
|
|
AutoCommit => 1, |
503
|
|
|
|
|
|
|
ref $attr ? %$attr : (), |
504
|
|
|
|
|
|
|
# attributes in DSN take precedence over \%attr connect parameter |
505
|
|
|
|
|
|
|
$driver_attrib_spec ? (split /\s*=>?\s*|\s*,\s*/, $driver_attrib_spec, -1) : (), |
506
|
|
|
|
|
|
|
); |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
$attr = \%attributes; # now set $attr to refer to our local copy |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
my $drh = $DBI::installed_drh{$driver} || $class->install_driver($driver) |
511
|
|
|
|
|
|
|
or die "panic: $class->install_driver($driver) failed"; |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
# attributes in DSN take precedence over \%attr connect parameter |
514
|
|
|
|
|
|
|
$user = $attr->{Username} if defined $attr->{Username}; |
515
|
|
|
|
|
|
|
$pass = $attr->{Password} if defined $attr->{Password}; |
516
|
|
|
|
|
|
|
delete $attr->{Password}; # always delete Password as closure stores it securely |
517
|
|
|
|
|
|
|
if ( !(defined $user && defined $pass) ) { |
518
|
|
|
|
|
|
|
($user, $pass) = $drh->default_user($user, $pass, $attr); |
519
|
|
|
|
|
|
|
} |
520
|
|
|
|
|
|
|
$attr->{Username} = $user; # force the Username to be the actual one used |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
my $connect_closure = sub { |
523
|
|
|
|
|
|
|
my ($old_dbh, $override_attr) = @_; |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
#use Data::Dumper; |
526
|
|
|
|
|
|
|
#warn "connect_closure: ".Data::Dumper::Dumper([$attr,\%attributes, $override_attr]); |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
my $dbh; |
529
|
|
|
|
|
|
|
unless ($dbh = $drh->$connect_meth($dsn, $user, $pass, $attr)) { |
530
|
|
|
|
|
|
|
$user = '' if !defined $user; |
531
|
|
|
|
|
|
|
$dsn = '' if !defined $dsn; |
532
|
|
|
|
|
|
|
# $drh->errstr isn't safe here because $dbh->DESTROY may not have |
533
|
|
|
|
|
|
|
# been called yet and so the dbh errstr would not have been copied |
534
|
|
|
|
|
|
|
# up to the drh errstr. Certainly true for connect_cached! |
535
|
|
|
|
|
|
|
my $errstr = $DBI::errstr; |
536
|
|
|
|
|
|
|
# Getting '(no error string)' here is a symptom of a ref loop |
537
|
|
|
|
|
|
|
$errstr = '(no error string)' if !defined $errstr; |
538
|
|
|
|
|
|
|
my $msg = "$class connect('$dsn','$user',...) failed: $errstr"; |
539
|
|
|
|
|
|
|
DBI->trace_msg(" $msg\n"); |
540
|
|
|
|
|
|
|
# XXX HandleWarn |
541
|
|
|
|
|
|
|
unless ($attr->{HandleError} && $attr->{HandleError}->($msg, $drh, $dbh)) { |
542
|
|
|
|
|
|
|
Carp::croak($msg) if $attr->{RaiseError}; |
543
|
|
|
|
|
|
|
Carp::carp ($msg) if $attr->{PrintError}; |
544
|
|
|
|
|
|
|
} |
545
|
|
|
|
|
|
|
$! = 0; # for the daft people who do DBI->connect(...) || die "$!"; |
546
|
|
|
|
|
|
|
return $dbh; # normally undef, but HandleError could change it |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
# merge any attribute overrides but don't change $attr itself (for closure) |
550
|
|
|
|
|
|
|
my $apply = { ($override_attr) ? (%$attr, %$override_attr ) : %$attr }; |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
# handle basic RootClass subclassing: |
553
|
|
|
|
|
|
|
my $rebless_class = $apply->{RootClass} || ($class ne 'DBI' ? $class : ''); |
554
|
|
|
|
|
|
|
if ($rebless_class) { |
555
|
|
|
|
|
|
|
no strict 'refs'; |
556
|
|
|
|
|
|
|
if ($apply->{RootClass}) { # explicit attribute (ie not static methd call class) |
557
|
|
|
|
|
|
|
delete $apply->{RootClass}; |
558
|
|
|
|
|
|
|
DBI::_load_class($rebless_class, 0); |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
unless (@{"$rebless_class\::db::ISA"} && @{"$rebless_class\::st::ISA"}) { |
561
|
|
|
|
|
|
|
Carp::carp("DBI subclasses '$rebless_class\::db' and ::st are not setup, RootClass ignored"); |
562
|
|
|
|
|
|
|
$rebless_class = undef; |
563
|
|
|
|
|
|
|
$class = 'DBI'; |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
else { |
566
|
|
|
|
|
|
|
$dbh->{RootClass} = $rebless_class; # $dbh->STORE called via plain DBI::db |
567
|
|
|
|
|
|
|
DBI::_set_isa([$rebless_class], 'DBI'); # sets up both '::db' and '::st' |
568
|
|
|
|
|
|
|
DBI::_rebless($dbh, $rebless_class); # appends '::db' |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
if (%$apply) { |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
if ($apply->{DbTypeSubclass}) { |
575
|
|
|
|
|
|
|
my $DbTypeSubclass = delete $apply->{DbTypeSubclass}; |
576
|
|
|
|
|
|
|
DBI::_rebless_dbtype_subclass($dbh, $rebless_class||$class, $DbTypeSubclass); |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
my $a; |
579
|
|
|
|
|
|
|
foreach $a (qw(Profile RaiseError PrintError AutoCommit)) { # do these first |
580
|
|
|
|
|
|
|
next unless exists $apply->{$a}; |
581
|
|
|
|
|
|
|
$dbh->{$a} = delete $apply->{$a}; |
582
|
|
|
|
|
|
|
} |
583
|
|
|
|
|
|
|
while ( my ($a, $v) = each %$apply) { |
584
|
|
|
|
|
|
|
eval { $dbh->{$a} = $v }; # assign in void context to avoid re-FETCH |
585
|
|
|
|
|
|
|
warn $@ if $@; |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
} |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
# confirm to driver (ie if subclassed) that we've connected sucessfully |
590
|
|
|
|
|
|
|
# and finished the attribute setup. pass in the original arguments |
591
|
|
|
|
|
|
|
$dbh->connected(@orig_args); #if ref $dbh ne 'DBI::db' or $proxy; |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
DBI->trace_msg(" <- connect= $dbh\n") if $DBI::dbi_debug; |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
return $dbh; |
596
|
|
|
|
|
|
|
}; |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
my $dbh = &$connect_closure(undef, undef); |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
$dbh->{dbi_connect_closure} = $connect_closure if $dbh; |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
return $dbh; |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
sub disconnect_all { |
607
|
|
|
|
|
|
|
keys %DBI::installed_drh; # reset iterator |
608
|
|
|
|
|
|
|
while ( my ($name, $drh) = each %DBI::installed_drh ) { |
609
|
|
|
|
|
|
|
$drh->disconnect_all() if ref $drh; |
610
|
|
|
|
|
|
|
} |
611
|
|
|
|
|
|
|
} |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
sub disconnect { # a regular beginners bug |
615
|
|
|
|
|
|
|
Carp::croak("DBI->disconnect is not a DBI method (read the DBI manual)"); |
616
|
|
|
|
|
|
|
} |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
sub install_driver { # croaks on failure |
620
|
|
|
|
|
|
|
my $class = shift; |
621
|
|
|
|
|
|
|
my($driver, $attr) = @_; |
622
|
|
|
|
|
|
|
my $drh; |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
$driver ||= $ENV{DBI_DRIVER} || ''; |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
# allow driver to be specified as a 'dbi:driver:' string |
627
|
|
|
|
|
|
|
$driver = $1 if $driver =~ s/^DBI:(.*?)://i; |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
Carp::croak("usage: $class->install_driver(\$driver [, \%attr])") |
630
|
|
|
|
|
|
|
unless ($driver and @_<=3); |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
# already installed |
633
|
|
|
|
|
|
|
return $drh if $drh = $DBI::installed_drh{$driver}; |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
$class->trace_msg(" -> $class->install_driver($driver" |
636
|
|
|
|
|
|
|
.") for $^O perl=$] pid=$$ ruid=$< euid=$>\n") |
637
|
|
|
|
|
|
|
if $DBI::dbi_debug; |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
# --- load the code |
640
|
|
|
|
|
|
|
my $driver_class = "DBD::$driver"; |
641
|
|
|
|
|
|
|
eval qq{package # hide from PAUSE |
642
|
|
|
|
|
|
|
DBI::_firesafe; # just in case |
643
|
|
|
|
|
|
|
require $driver_class; # load the driver |
644
|
|
|
|
|
|
|
}; |
645
|
|
|
|
|
|
|
if ($@) { |
646
|
|
|
|
|
|
|
my $err = $@; |
647
|
|
|
|
|
|
|
my $advice = ""; |
648
|
|
|
|
|
|
|
if ($err =~ /Can't find loadable object/) { |
649
|
|
|
|
|
|
|
$advice = "Perhaps DBD::$driver was statically linked into a new perl binary." |
650
|
|
|
|
|
|
|
."\nIn which case you need to use that new perl binary." |
651
|
|
|
|
|
|
|
."\nOr perhaps only the .pm file was installed but not the shared object file." |
652
|
|
|
|
|
|
|
} |
653
|
|
|
|
|
|
|
elsif ($err =~ /Can't locate.*?DBD\/$driver\.pm in \@INC/) { |
654
|
|
|
|
|
|
|
my @drv = $class->available_drivers(1); |
655
|
|
|
|
|
|
|
$advice = "Perhaps the DBD::$driver perl module hasn't been fully installed,\n" |
656
|
|
|
|
|
|
|
."or perhaps the capitalisation of '$driver' isn't right.\n" |
657
|
|
|
|
|
|
|
."Available drivers: ".join(", ", @drv)."."; |
658
|
|
|
|
|
|
|
} |
659
|
|
|
|
|
|
|
elsif ($err =~ /Can't load .*? for module DBD::/) { |
660
|
|
|
|
|
|
|
$advice = "Perhaps a required shared library or dll isn't installed where expected"; |
661
|
|
|
|
|
|
|
} |
662
|
|
|
|
|
|
|
elsif ($err =~ /Can't locate .*? in \@INC/) { |
663
|
|
|
|
|
|
|
$advice = "Perhaps a module that DBD::$driver requires hasn't been fully installed"; |
664
|
|
|
|
|
|
|
} |
665
|
|
|
|
|
|
|
Carp::croak("install_driver($driver) failed: $err$advice\n"); |
666
|
|
|
|
|
|
|
} |
667
|
|
|
|
|
|
|
if ($DBI::dbi_debug) { |
668
|
|
|
|
|
|
|
no strict 'refs'; |
669
|
|
|
|
|
|
|
(my $driver_file = $driver_class) =~ s/::/\//g; |
670
|
|
|
|
|
|
|
my $dbd_ver = ${"$driver_class\::VERSION"} || "undef"; |
671
|
|
|
|
|
|
|
$class->trace_msg(" install_driver: $driver_class version $dbd_ver" |
672
|
|
|
|
|
|
|
." loaded from $INC{qq($driver_file.pm)}\n"); |
673
|
|
|
|
|
|
|
} |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
# --- do some behind-the-scenes checks and setups on the driver |
676
|
|
|
|
|
|
|
$class->setup_driver($driver_class); |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
# --- run the driver function |
679
|
|
|
|
|
|
|
$drh = eval { $driver_class->driver($attr || {}) }; |
680
|
|
|
|
|
|
|
unless ($drh && ref $drh && !$@) { |
681
|
|
|
|
|
|
|
my $advice = ""; |
682
|
|
|
|
|
|
|
$@ ||= "$driver_class->driver didn't return a handle"; |
683
|
|
|
|
|
|
|
# catch people on case in-sensitive systems using the wrong case |
684
|
|
|
|
|
|
|
$advice = "\nPerhaps the capitalisation of DBD '$driver' isn't right." |
685
|
|
|
|
|
|
|
if $@ =~ /locate object method/; |
686
|
|
|
|
|
|
|
Carp::croak("$driver_class initialisation failed: $@$advice"); |
687
|
|
|
|
|
|
|
} |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
$DBI::installed_drh{$driver} = $drh; |
690
|
|
|
|
|
|
|
$class->trace_msg(" <- install_driver= $drh\n") if $DBI::dbi_debug; |
691
|
|
|
|
|
|
|
$drh; |
692
|
|
|
|
|
|
|
} |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
*driver = \&install_driver; # currently an alias, may change |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
sub setup_driver { |
698
|
|
|
|
|
|
|
my ($class, $driver_class) = @_; |
699
|
|
|
|
|
|
|
my $type; |
700
|
|
|
|
|
|
|
foreach $type (qw(dr db st)){ |
701
|
|
|
|
|
|
|
my $class = $driver_class."::$type"; |
702
|
|
|
|
|
|
|
no strict 'refs'; |
703
|
|
|
|
|
|
|
push @{"${class}::ISA"}, "DBD::_::$type" |
704
|
|
|
|
|
|
|
unless UNIVERSAL::isa($class, "DBD::_::$type"); |
705
|
|
|
|
|
|
|
my $mem_class = "DBD::_mem::$type"; |
706
|
|
|
|
|
|
|
push @{"${class}_mem::ISA"}, $mem_class |
707
|
|
|
|
|
|
|
unless UNIVERSAL::isa("${class}_mem", $mem_class) |
708
|
|
|
|
|
|
|
or $DBI::PurePerl; |
709
|
|
|
|
|
|
|
} |
710
|
|
|
|
|
|
|
} |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
sub _rebless { |
714
|
|
|
|
|
|
|
my $dbh = shift; |
715
|
|
|
|
|
|
|
my ($outer, $inner) = DBI::_handles($dbh); |
716
|
|
|
|
|
|
|
my $class = shift(@_).'::db'; |
717
|
|
|
|
|
|
|
bless $inner => $class; |
718
|
|
|
|
|
|
|
bless $outer => $class; # outer last for return |
719
|
|
|
|
|
|
|
} |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
sub _set_isa { |
723
|
|
|
|
|
|
|
my ($classes, $topclass) = @_; |
724
|
|
|
|
|
|
|
my $trace = DBI->trace_msg(" _set_isa([@$classes])\n"); |
725
|
|
|
|
|
|
|
foreach my $suffix ('::db','::st') { |
726
|
|
|
|
|
|
|
my $previous = $topclass || 'DBI'; # trees are rooted here |
727
|
|
|
|
|
|
|
foreach my $class (@$classes) { |
728
|
|
|
|
|
|
|
my $base_class = $previous.$suffix; |
729
|
|
|
|
|
|
|
my $sub_class = $class.$suffix; |
730
|
|
|
|
|
|
|
my $sub_class_isa = "${sub_class}::ISA"; |
731
|
|
|
|
|
|
|
no strict 'refs'; |
732
|
|
|
|
|
|
|
if (@$sub_class_isa) { |
733
|
|
|
|
|
|
|
DBI->trace_msg(" $sub_class_isa skipped (already set to @$sub_class_isa)\n") |
734
|
|
|
|
|
|
|
if $trace; |
735
|
|
|
|
|
|
|
} |
736
|
|
|
|
|
|
|
else { |
737
|
|
|
|
|
|
|
@$sub_class_isa = ($base_class) unless @$sub_class_isa; |
738
|
|
|
|
|
|
|
DBI->trace_msg(" $sub_class_isa = $base_class\n") |
739
|
|
|
|
|
|
|
if $trace; |
740
|
|
|
|
|
|
|
} |
741
|
|
|
|
|
|
|
$previous = $class; |
742
|
|
|
|
|
|
|
} |
743
|
|
|
|
|
|
|
} |
744
|
|
|
|
|
|
|
} |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
sub _rebless_dbtype_subclass { |
748
|
|
|
|
|
|
|
my ($dbh, $rootclass, $DbTypeSubclass) = @_; |
749
|
|
|
|
|
|
|
# determine the db type names for class hierarchy |
750
|
|
|
|
|
|
|
my @hierarchy = DBI::_dbtype_names($dbh, $DbTypeSubclass); |
751
|
|
|
|
|
|
|
# add the rootclass prefix to each ('DBI::' or 'MyDBI::' etc) |
752
|
|
|
|
|
|
|
$_ = $rootclass.'::'.$_ foreach (@hierarchy); |
753
|
|
|
|
|
|
|
# load the modules from the 'top down' |
754
|
|
|
|
|
|
|
DBI::_load_class($_, 1) foreach (reverse @hierarchy); |
755
|
|
|
|
|
|
|
# setup class hierarchy if needed, does both '::db' and '::st' |
756
|
|
|
|
|
|
|
DBI::_set_isa(\@hierarchy, $rootclass); |
757
|
|
|
|
|
|
|
# finally bless the handle into the subclass |
758
|
|
|
|
|
|
|
DBI::_rebless($dbh, $hierarchy[0]); |
759
|
|
|
|
|
|
|
} |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
sub _dbtype_names { # list dbtypes for hierarchy, ie Informix=>ADO=>ODBC |
763
|
|
|
|
|
|
|
my ($dbh, $DbTypeSubclass) = @_; |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
if ($DbTypeSubclass && $DbTypeSubclass ne '1' && ref $DbTypeSubclass ne 'CODE') { |
766
|
|
|
|
|
|
|
# treat $DbTypeSubclass as a comma separated list of names |
767
|
|
|
|
|
|
|
my @dbtypes = split /\s*,\s*/, $DbTypeSubclass; |
768
|
|
|
|
|
|
|
$dbh->trace_msg(" DbTypeSubclass($DbTypeSubclass)=@dbtypes (explicit)\n"); |
769
|
|
|
|
|
|
|
return @dbtypes; |
770
|
|
|
|
|
|
|
} |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
# XXX will call $dbh->get_info(17) (=SQL_DBMS_NAME) in future? |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
my $driver = $dbh->{Driver}->{Name}; |
775
|
|
|
|
|
|
|
if ( $driver eq 'Proxy' ) { |
776
|
|
|
|
|
|
|
# XXX Looking into the internals of DBD::Proxy is questionable! |
777
|
|
|
|
|
|
|
($driver) = $dbh->{proxy_client}->{application} =~ /^DBI:(.+?):/i |
778
|
|
|
|
|
|
|
or die "Can't determine driver name from proxy"; |
779
|
|
|
|
|
|
|
} |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
my @dbtypes = (ucfirst($driver)); |
782
|
|
|
|
|
|
|
if ($driver eq 'ODBC' || $driver eq 'ADO') { |
783
|
|
|
|
|
|
|
# XXX will move these out and make extensible later: |
784
|
|
|
|
|
|
|
my $_dbtype_name_regexp = 'Oracle'; # eg 'Oracle|Foo|Bar' |
785
|
|
|
|
|
|
|
my %_dbtype_name_map = ( |
786
|
|
|
|
|
|
|
'Microsoft SQL Server' => 'MSSQL', |
787
|
|
|
|
|
|
|
'SQL Server' => 'Sybase', |
788
|
|
|
|
|
|
|
'Adaptive Server Anywhere' => 'ASAny', |
789
|
|
|
|
|
|
|
'ADABAS D' => 'AdabasD', |
790
|
|
|
|
|
|
|
); |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
my $name; |
793
|
|
|
|
|
|
|
$name = $dbh->func(17, 'GetInfo') # SQL_DBMS_NAME |
794
|
|
|
|
|
|
|
if $driver eq 'ODBC'; |
795
|
|
|
|
|
|
|
$name = $dbh->{ado_conn}->Properties->Item('DBMS Name')->Value |
796
|
|
|
|
|
|
|
if $driver eq 'ADO'; |
797
|
|
|
|
|
|
|
die "Can't determine driver name! ($DBI::errstr)\n" |
798
|
|
|
|
|
|
|
unless $name; |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
my $dbtype; |
801
|
|
|
|
|
|
|
if ($_dbtype_name_map{$name}) { |
802
|
|
|
|
|
|
|
$dbtype = $_dbtype_name_map{$name}; |
803
|
|
|
|
|
|
|
} |
804
|
|
|
|
|
|
|
else { |
805
|
|
|
|
|
|
|
if ($name =~ /($_dbtype_name_regexp)/) { |
806
|
|
|
|
|
|
|
$dbtype = lc($1); |
807
|
|
|
|
|
|
|
} |
808
|
|
|
|
|
|
|
else { # generic mangling for other names: |
809
|
|
|
|
|
|
|
$dbtype = lc($name); |
810
|
|
|
|
|
|
|
} |
811
|
|
|
|
|
|
|
$dbtype =~ s/\b(\w)/\U$1/g; |
812
|
|
|
|
|
|
|
$dbtype =~ s/\W+/_/g; |
813
|
|
|
|
|
|
|
} |
814
|
|
|
|
|
|
|
# add ODBC 'behind' ADO |
815
|
|
|
|
|
|
|
push @dbtypes, 'ODBC' if $driver eq 'ADO'; |
816
|
|
|
|
|
|
|
# add discovered dbtype in front of ADO/ODBC |
817
|
|
|
|
|
|
|
unshift @dbtypes, $dbtype; |
818
|
|
|
|
|
|
|
} |
819
|
|
|
|
|
|
|
@dbtypes = &$DbTypeSubclass($dbh, \@dbtypes) |
820
|
|
|
|
|
|
|
if (ref $DbTypeSubclass eq 'CODE'); |
821
|
|
|
|
|
|
|
$dbh->trace_msg(" DbTypeSubclass($DbTypeSubclass)=@dbtypes\n"); |
822
|
|
|
|
|
|
|
return @dbtypes; |
823
|
|
|
|
|
|
|
} |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
sub _load_class { |
826
|
|
|
|
|
|
|
my ($load_class, $missing_ok) = @_; |
827
|
|
|
|
|
|
|
DBI->trace_msg(" _load_class($load_class, $missing_ok)\n", 2); |
828
|
|
|
|
|
|
|
no strict 'refs'; |
829
|
|
|
|
|
|
|
return 1 if @{"$load_class\::ISA"}; # already loaded/exists |
830
|
|
|
|
|
|
|
(my $module = $load_class) =~ s!::!/!g; |
831
|
|
|
|
|
|
|
DBI->trace_msg(" _load_class require $module\n", 2); |
832
|
|
|
|
|
|
|
eval { require "$module.pm"; }; |
833
|
|
|
|
|
|
|
return 1 unless $@; |
834
|
|
|
|
|
|
|
return 0 if $missing_ok && $@ =~ /^Can't locate \Q$module.pm\E/; |
835
|
|
|
|
|
|
|
die $@; |
836
|
|
|
|
|
|
|
} |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
sub init_rootclass { # deprecated |
840
|
|
|
|
|
|
|
return 1; |
841
|
|
|
|
|
|
|
} |
842
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
*internal = \&DBD::Switch::dr::driver; |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
sub driver_prefix { |
847
|
|
|
|
|
|
|
my ($class, $driver) = @_; |
848
|
|
|
|
|
|
|
return $dbd_class_registry{$driver}->{prefix} if exists $dbd_class_registry{$driver}; |
849
|
|
|
|
|
|
|
return; |
850
|
|
|
|
|
|
|
} |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
sub available_drivers { |
853
|
|
|
|
|
|
|
my($quiet) = @_; |
854
|
|
|
|
|
|
|
my(@drivers, $d, $f); |
855
|
|
|
|
|
|
|
local(*DBI::DIR, $@); |
856
|
|
|
|
|
|
|
my(%seen_dir, %seen_dbd); |
857
|
|
|
|
|
|
|
my $haveFileSpec = eval { require File::Spec }; |
858
|
|
|
|
|
|
|
foreach $d (@INC){ |
859
|
|
|
|
|
|
|
chomp($d); # Perl 5 beta 3 bug in #!./perl -Ilib from Test::Harness |
860
|
|
|
|
|
|
|
my $dbd_dir = |
861
|
|
|
|
|
|
|
($haveFileSpec ? File::Spec->catdir($d, 'DBD') : "$d/DBD"); |
862
|
|
|
|
|
|
|
next unless -d $dbd_dir; |
863
|
|
|
|
|
|
|
next if $seen_dir{$d}; |
864
|
|
|
|
|
|
|
$seen_dir{$d} = 1; |
865
|
|
|
|
|
|
|
# XXX we have a problem here with case insensitive file systems |
866
|
|
|
|
|
|
|
# XXX since we can't tell what case must be used when loading. |
867
|
|
|
|
|
|
|
opendir(DBI::DIR, $dbd_dir) || Carp::carp "opendir $dbd_dir: $!\n"; |
868
|
|
|
|
|
|
|
foreach $f (readdir(DBI::DIR)){ |
869
|
|
|
|
|
|
|
next unless $f =~ s/\.pm$//; |
870
|
|
|
|
|
|
|
next if $f eq 'NullP'; |
871
|
|
|
|
|
|
|
if ($seen_dbd{$f}){ |
872
|
|
|
|
|
|
|
Carp::carp "DBD::$f in $d is hidden by DBD::$f in $seen_dbd{$f}\n" |
873
|
|
|
|
|
|
|
unless $quiet; |
874
|
|
|
|
|
|
|
} else { |
875
|
|
|
|
|
|
|
push(@drivers, $f); |
876
|
|
|
|
|
|
|
} |
877
|
|
|
|
|
|
|
$seen_dbd{$f} = $d; |
878
|
|
|
|
|
|
|
} |
879
|
|
|
|
|
|
|
closedir(DBI::DIR); |
880
|
|
|
|
|
|
|
} |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
# "return sort @drivers" will not DWIM in scalar context. |
883
|
|
|
|
|
|
|
return wantarray ? sort @drivers : @drivers; |
884
|
|
|
|
|
|
|
} |
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
sub installed_versions { |
887
|
|
|
|
|
|
|
my ($class, $quiet) = @_; |
888
|
|
|
|
|
|
|
my %error; |
889
|
|
|
|
|
|
|
my %version = ( DBI => $DBI::VERSION ); |
890
|
|
|
|
|
|
|
$version{"DBI::PurePerl"} = $DBI::PurePerl::VERSION |
891
|
|
|
|
|
|
|
if $DBI::PurePerl; |
892
|
|
|
|
|
|
|
for my $driver ($class->available_drivers($quiet)) { |
893
|
|
|
|
|
|
|
next if $DBI::PurePerl && grep { -d "$_/auto/DBD/$driver" } @INC; |
894
|
|
|
|
|
|
|
my $drh = eval { |
895
|
|
|
|
|
|
|
local $SIG{__WARN__} = sub {}; |
896
|
|
|
|
|
|
|
$class->install_driver($driver); |
897
|
|
|
|
|
|
|
}; |
898
|
|
|
|
|
|
|
($error{"DBD::$driver"}=$@),next if $@; |
899
|
|
|
|
|
|
|
no strict 'refs'; |
900
|
|
|
|
|
|
|
my $vers = ${"DBD::$driver" . '::VERSION'}; |
901
|
|
|
|
|
|
|
$version{"DBD::$driver"} = $vers || '?'; |
902
|
|
|
|
|
|
|
} |
903
|
|
|
|
|
|
|
if (wantarray) { |
904
|
|
|
|
|
|
|
return map { m/^DBD::(\w+)/ ? ($1) : () } sort keys %version; |
905
|
|
|
|
|
|
|
} |
906
|
|
|
|
|
|
|
if (!defined wantarray) { # void context |
907
|
|
|
|
|
|
|
require Config; # add more detail |
908
|
|
|
|
|
|
|
$version{OS} = "$^O\t($Config::Config{osvers})"; |
909
|
|
|
|
|
|
|
$version{Perl} = "$]\t($Config::Config{archname})"; |
910
|
|
|
|
|
|
|
$version{$_} = (($error{$_} =~ s/ \(\@INC.*//s),$error{$_}) |
911
|
|
|
|
|
|
|
for keys %error; |
912
|
|
|
|
|
|
|
printf " %-16s: %s\n",$_,$version{$_} |
913
|
|
|
|
|
|
|
for reverse sort keys %version; |
914
|
|
|
|
|
|
|
} |
915
|
|
|
|
|
|
|
return \%version; |
916
|
|
|
|
|
|
|
} |
917
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
sub data_sources { |
920
|
|
|
|
|
|
|
my ($class, $driver, @other) = @_; |
921
|
|
|
|
|
|
|
my $drh = $class->install_driver($driver); |
922
|
|
|
|
|
|
|
my @ds = $drh->data_sources(@other); |
923
|
|
|
|
|
|
|
return @ds; |
924
|
|
|
|
|
|
|
} |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
sub neat_list { |
928
|
|
|
|
|
|
|
my ($listref, $maxlen, $sep) = @_; |
929
|
|
|
|
|
|
|
$maxlen = 0 unless defined $maxlen; # 0 == use internal default |
930
|
|
|
|
|
|
|
$sep = ", " unless defined $sep; |
931
|
|
|
|
|
|
|
join($sep, map { neat($_,$maxlen) } @$listref); |
932
|
|
|
|
|
|
|
} |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
sub dump_results { # also aliased as a method in DBD::_::st |
936
|
|
|
|
|
|
|
my ($sth, $maxlen, $lsep, $fsep, $fh) = @_; |
937
|
|
|
|
|
|
|
return 0 unless $sth; |
938
|
|
|
|
|
|
|
$maxlen ||= 35; |
939
|
|
|
|
|
|
|
$lsep ||= "\n"; |
940
|
|
|
|
|
|
|
$fh ||= \*STDOUT; |
941
|
|
|
|
|
|
|
my $rows = 0; |
942
|
|
|
|
|
|
|
my $ref; |
943
|
|
|
|
|
|
|
while($ref = $sth->fetch) { |
944
|
|
|
|
|
|
|
print $fh $lsep if $rows++ and $lsep; |
945
|
|
|
|
|
|
|
my $str = neat_list($ref,$maxlen,$fsep); |
946
|
|
|
|
|
|
|
print $fh $str; # done on two lines to avoid 5.003 errors |
947
|
|
|
|
|
|
|
} |
948
|
|
|
|
|
|
|
print $fh "\n$rows rows".($DBI::err ? " ($DBI::err: $DBI::errstr)" : "")."\n"; |
949
|
|
|
|
|
|
|
$rows; |
950
|
|
|
|
|
|
|
} |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
sub data_diff { |
954
|
|
|
|
|
|
|
my ($a, $b, $logical) = @_; |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
my $diff = data_string_diff($a, $b); |
957
|
|
|
|
|
|
|
return "" if $logical and !$diff; |
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
my $a_desc = data_string_desc($a); |
960
|
|
|
|
|
|
|
my $b_desc = data_string_desc($b); |
961
|
|
|
|
|
|
|
return "" if !$diff and $a_desc eq $b_desc; |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
$diff ||= "Strings contain the same sequence of characters" |
964
|
|
|
|
|
|
|
if length($a); |
965
|
|
|
|
|
|
|
$diff .= "\n" if $diff; |
966
|
|
|
|
|
|
|
return "a: $a_desc\nb: $b_desc\n$diff"; |
967
|
|
|
|
|
|
|
} |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
sub data_string_diff { |
971
|
|
|
|
|
|
|
# Compares 'logical' characters, not bytes, so a latin1 string and an |
972
|
|
|
|
|
|
|
# an equivalent Unicode string will compare as equal even though their |
973
|
|
|
|
|
|
|
# byte encodings are different. |
974
|
|
|
|
|
|
|
my ($a, $b) = @_; |
975
|
|
|
|
|
|
|
unless (defined $a and defined $b) { # one undef |
976
|
|
|
|
|
|
|
return "" |
977
|
|
|
|
|
|
|
if !defined $a and !defined $b; |
978
|
|
|
|
|
|
|
return "String a is undef, string b has ".length($b)." characters" |
979
|
|
|
|
|
|
|
if !defined $a; |
980
|
|
|
|
|
|
|
return "String b is undef, string a has ".length($a)." characters" |
981
|
|
|
|
|
|
|
if !defined $b; |
982
|
|
|
|
|
|
|
} |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
require utf8; |
985
|
|
|
|
|
|
|
# hack to cater for perl 5.6 |
986
|
|
|
|
|
|
|
*utf8::is_utf8 = sub { (DBI::neat(shift)=~/^"/) } unless defined &utf8::is_utf8; |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
my @a_chars = (utf8::is_utf8($a)) ? unpack("U*", $a) : unpack("C*", $a); |
989
|
|
|
|
|
|
|
my @b_chars = (utf8::is_utf8($b)) ? unpack("U*", $b) : unpack("C*", $b); |
990
|
|
|
|
|
|
|
my $i = 0; |
991
|
|
|
|
|
|
|
while (@a_chars && @b_chars) { |
992
|
|
|
|
|
|
|
++$i, shift(@a_chars), shift(@b_chars), next |
993
|
|
|
|
|
|
|
if $a_chars[0] == $b_chars[0];# compare ordinal values |
994
|
|
|
|
|
|
|
my @desc = map { |
995
|
|
|
|
|
|
|
$_ > 255 ? # if wide character... |
996
|
|
|
|
|
|
|
sprintf("\\x{%04X}", $_) : # \x{...} |
997
|
|
|
|
|
|
|
chr($_) =~ /[[:cntrl:]]/ ? # else if control character ... |
998
|
|
|
|
|
|
|
sprintf("\\x%02X", $_) : # \x.. |
999
|
|
|
|
|
|
|
chr($_) # else as themselves |
1000
|
|
|
|
|
|
|
} ($a_chars[0], $b_chars[0]); |
1001
|
|
|
|
|
|
|
# highlight probable double-encoding? |
1002
|
|
|
|
|
|
|
foreach my $c ( @desc ) { |
1003
|
|
|
|
|
|
|
next unless $c =~ m/\\x\{08(..)}/; |
1004
|
|
|
|
|
|
|
$c .= "='" .chr(hex($1)) ."'" |
1005
|
|
|
|
|
|
|
} |
1006
|
|
|
|
|
|
|
return sprintf "Strings differ at index $i: a[$i]=$desc[0], b[$i]=$desc[1]"; |
1007
|
|
|
|
|
|
|
} |
1008
|
|
|
|
|
|
|
return "String a truncated after $i characters" if @b_chars; |
1009
|
|
|
|
|
|
|
return "String b truncated after $i characters" if @a_chars; |
1010
|
|
|
|
|
|
|
return ""; |
1011
|
|
|
|
|
|
|
} |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
sub data_string_desc { # describe a data string |
1015
|
|
|
|
|
|
|
my ($a) = @_; |
1016
|
|
|
|
|
|
|
require bytes; |
1017
|
|
|
|
|
|
|
require utf8; |
1018
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
# hacks to cater for perl 5.6 |
1020
|
|
|
|
|
|
|
*utf8::is_utf8 = sub { (DBI::neat(shift)=~/^"/) } unless defined &utf8::is_utf8; |
1021
|
|
|
|
|
|
|
*utf8::valid = sub { 1 } unless defined &utf8::valid; |
1022
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
# Give sufficient info to help diagnose at least these kinds of situations: |
1024
|
|
|
|
|
|
|
# - valid UTF8 byte sequence but UTF8 flag not set |
1025
|
|
|
|
|
|
|
# (might be ascii so also need to check for hibit to make it worthwhile) |
1026
|
|
|
|
|
|
|
# - UTF8 flag set but invalid UTF8 byte sequence |
1027
|
|
|
|
|
|
|
# could do better here, but this'll do for now |
1028
|
|
|
|
|
|
|
my $utf8 = sprintf "UTF8 %s%s", |
1029
|
|
|
|
|
|
|
utf8::is_utf8($a) ? "on" : "off", |
1030
|
|
|
|
|
|
|
utf8::valid($a||'') ? "" : " but INVALID encoding"; |
1031
|
|
|
|
|
|
|
return "$utf8, undef" unless defined $a; |
1032
|
|
|
|
|
|
|
my $is_ascii = $a =~ m/^[\000-\177]*$/; |
1033
|
|
|
|
|
|
|
return sprintf "%s, %s, %d characters %d bytes", |
1034
|
|
|
|
|
|
|
$utf8, $is_ascii ? "ASCII" : "non-ASCII", |
1035
|
|
|
|
|
|
|
length($a), bytes::length($a); |
1036
|
|
|
|
|
|
|
} |
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
sub connect_test_perf { |
1040
|
|
|
|
|
|
|
my($class, $dsn,$dbuser,$dbpass, $attr) = @_; |
1041
|
|
|
|
|
|
|
Carp::croak("connect_test_perf needs hash ref as fourth arg") unless ref $attr; |
1042
|
|
|
|
|
|
|
# these are non standard attributes just for this special method |
1043
|
|
|
|
|
|
|
my $loops ||= $attr->{dbi_loops} || 5; |
1044
|
|
|
|
|
|
|
my $par ||= $attr->{dbi_par} || 1; # parallelism |
1045
|
|
|
|
|
|
|
my $verb ||= $attr->{dbi_verb} || 1; |
1046
|
|
|
|
|
|
|
my $meth ||= $attr->{dbi_meth} || 'connect'; |
1047
|
|
|
|
|
|
|
print "$dsn: testing $loops sets of $par connections:\n"; |
1048
|
|
|
|
|
|
|
require "FileHandle.pm"; # don't let toke.c create empty FileHandle package |
1049
|
|
|
|
|
|
|
local $| = 1; |
1050
|
|
|
|
|
|
|
my $drh = $class->install_driver($dsn) or Carp::croak("Can't install $dsn driver\n"); |
1051
|
|
|
|
|
|
|
# test the connection and warm up caches etc |
1052
|
|
|
|
|
|
|
$drh->connect($dsn,$dbuser,$dbpass) or Carp::croak("connect failed: $DBI::errstr"); |
1053
|
|
|
|
|
|
|
my $t1 = dbi_time(); |
1054
|
|
|
|
|
|
|
my $loop; |
1055
|
|
|
|
|
|
|
for $loop (1..$loops) { |
1056
|
|
|
|
|
|
|
my @cons; |
1057
|
|
|
|
|
|
|
print "Connecting... " if $verb; |
1058
|
|
|
|
|
|
|
for (1..$par) { |
1059
|
|
|
|
|
|
|
print "$_ "; |
1060
|
|
|
|
|
|
|
push @cons, ($drh->connect($dsn,$dbuser,$dbpass) |
1061
|
|
|
|
|
|
|
or Carp::croak("connect failed: $DBI::errstr\n")); |
1062
|
|
|
|
|
|
|
} |
1063
|
|
|
|
|
|
|
print "\nDisconnecting...\n" if $verb; |
1064
|
|
|
|
|
|
|
for (@cons) { |
1065
|
|
|
|
|
|
|
$_->disconnect or warn "disconnect failed: $DBI::errstr" |
1066
|
|
|
|
|
|
|
} |
1067
|
|
|
|
|
|
|
} |
1068
|
|
|
|
|
|
|
my $t2 = dbi_time(); |
1069
|
|
|
|
|
|
|
my $td = $t2 - $t1; |
1070
|
|
|
|
|
|
|
printf "$meth %d and disconnect them, %d times: %.4fs / %d = %.4fs\n", |
1071
|
|
|
|
|
|
|
$par, $loops, $td, $loops*$par, $td/($loops*$par); |
1072
|
|
|
|
|
|
|
return $td; |
1073
|
|
|
|
|
|
|
} |
1074
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
# Help people doing DBI->errstr, might even document it one day |
1077
|
|
|
|
|
|
|
# XXX probably best moved to cheaper XS code if this gets documented |
1078
|
|
|
|
|
|
|
sub err { $DBI::err } |
1079
|
|
|
|
|
|
|
sub errstr { $DBI::errstr } |
1080
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
# --- Private Internal Function for Creating New DBI Handles |
1083
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
# XXX move to PurePerl? |
1085
|
|
|
|
|
|
|
*DBI::dr::TIEHASH = \&DBI::st::TIEHASH; |
1086
|
|
|
|
|
|
|
*DBI::db::TIEHASH = \&DBI::st::TIEHASH; |
1087
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
# These three special constructors are called by the drivers |
1090
|
|
|
|
|
|
|
# The way they are called is likely to change. |
1091
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
our $shared_profile; |
1093
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
sub _new_drh { # called by DBD::::driver() |
1095
|
|
|
|
|
|
|
my ($class, $initial_attr, $imp_data) = @_; |
1096
|
|
|
|
|
|
|
# Provide default storage for State,Err and Errstr. |
1097
|
|
|
|
|
|
|
# Note that these are shared by all child handles by default! XXX |
1098
|
|
|
|
|
|
|
# State must be undef to get automatic faking in DBI::var::FETCH |
1099
|
|
|
|
|
|
|
my ($h_state_store, $h_err_store, $h_errstr_store) = (undef, 0, ''); |
1100
|
|
|
|
|
|
|
my $attr = { |
1101
|
|
|
|
|
|
|
# these attributes get copied down to child handles by default |
1102
|
|
|
|
|
|
|
'State' => \$h_state_store, # Holder for DBI::state |
1103
|
|
|
|
|
|
|
'Err' => \$h_err_store, # Holder for DBI::err |
1104
|
|
|
|
|
|
|
'Errstr' => \$h_errstr_store, # Holder for DBI::errstr |
1105
|
|
|
|
|
|
|
'TraceLevel' => 0, |
1106
|
|
|
|
|
|
|
FetchHashKeyName=> 'NAME', |
1107
|
|
|
|
|
|
|
%$initial_attr, |
1108
|
|
|
|
|
|
|
}; |
1109
|
|
|
|
|
|
|
my ($h, $i) = _new_handle('DBI::dr', '', $attr, $imp_data, $class); |
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
# XXX DBI_PROFILE unless DBI::PurePerl because for some reason |
1112
|
|
|
|
|
|
|
# it kills the t/zz_*_pp.t tests (they silently exit early) |
1113
|
|
|
|
|
|
|
if (($ENV{DBI_PROFILE} && !$DBI::PurePerl) || $shared_profile) { |
1114
|
|
|
|
|
|
|
# The profile object created here when the first driver is loaded |
1115
|
|
|
|
|
|
|
# is shared by all drivers so we end up with just one set of profile |
1116
|
|
|
|
|
|
|
# data and thus the 'total time in DBI' is really the true total. |
1117
|
|
|
|
|
|
|
if (!$shared_profile) { # first time |
1118
|
|
|
|
|
|
|
$h->{Profile} = $ENV{DBI_PROFILE}; # write string |
1119
|
|
|
|
|
|
|
$shared_profile = $h->{Profile}; # read and record object |
1120
|
|
|
|
|
|
|
} |
1121
|
|
|
|
|
|
|
else { |
1122
|
|
|
|
|
|
|
$h->{Profile} = $shared_profile; |
1123
|
|
|
|
|
|
|
} |
1124
|
|
|
|
|
|
|
} |
1125
|
|
|
|
|
|
|
return $h unless wantarray; |
1126
|
|
|
|
|
|
|
($h, $i); |
1127
|
|
|
|
|
|
|
} |
1128
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
sub _new_dbh { # called by DBD::::dr::connect() |
1130
|
|
|
|
|
|
|
my ($drh, $attr, $imp_data) = @_; |
1131
|
|
|
|
|
|
|
my $imp_class = $drh->{ImplementorClass} |
1132
|
|
|
|
|
|
|
or Carp::croak("DBI _new_dbh: $drh has no ImplementorClass"); |
1133
|
|
|
|
|
|
|
substr($imp_class,-4,4) = '::db'; |
1134
|
|
|
|
|
|
|
my $app_class = ref $drh; |
1135
|
|
|
|
|
|
|
substr($app_class,-4,4) = '::db'; |
1136
|
|
|
|
|
|
|
$attr->{Err} ||= \my $err; |
1137
|
|
|
|
|
|
|
$attr->{Errstr} ||= \my $errstr; |
1138
|
|
|
|
|
|
|
$attr->{State} ||= \my $state; |
1139
|
|
|
|
|
|
|
_new_handle($app_class, $drh, $attr, $imp_data, $imp_class); |
1140
|
|
|
|
|
|
|
} |
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
sub _new_sth { # called by DBD::::db::prepare) |
1143
|
|
|
|
|
|
|
my ($dbh, $attr, $imp_data) = @_; |
1144
|
|
|
|
|
|
|
my $imp_class = $dbh->{ImplementorClass} |
1145
|
|
|
|
|
|
|
or Carp::croak("DBI _new_sth: $dbh has no ImplementorClass"); |
1146
|
|
|
|
|
|
|
substr($imp_class,-4,4) = '::st'; |
1147
|
|
|
|
|
|
|
my $app_class = ref $dbh; |
1148
|
|
|
|
|
|
|
substr($app_class,-4,4) = '::st'; |
1149
|
|
|
|
|
|
|
_new_handle($app_class, $dbh, $attr, $imp_data, $imp_class); |
1150
|
|
|
|
|
|
|
} |
1151
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
# end of DBI package |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
|
1156
|
|
|
|
|
|
|
|
1157
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
1158
|
|
|
|
|
|
|
# === The internal DBI Switch pseudo 'driver' class === |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
{ package # hide from PAUSE |
1161
|
|
|
|
|
|
|
DBD::Switch::dr; |
1162
|
|
|
|
|
|
|
DBI->setup_driver('DBD::Switch'); # sets up @ISA |
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
$DBD::Switch::dr::imp_data_size = 0; |
1165
|
|
|
|
|
|
|
$DBD::Switch::dr::imp_data_size = 0; # avoid typo warning |
1166
|
|
|
|
|
|
|
my $drh; |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
sub driver { |
1169
|
|
|
|
|
|
|
return $drh if $drh; # a package global |
1170
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
my $inner; |
1172
|
|
|
|
|
|
|
($drh, $inner) = DBI::_new_drh('DBD::Switch::dr', { |
1173
|
|
|
|
|
|
|
'Name' => 'Switch', |
1174
|
|
|
|
|
|
|
'Version' => $DBI::VERSION, |
1175
|
|
|
|
|
|
|
'Attribution' => "DBI $DBI::VERSION by Tim Bunce", |
1176
|
|
|
|
|
|
|
}); |
1177
|
|
|
|
|
|
|
Carp::croak("DBD::Switch init failed!") unless ($drh && $inner); |
1178
|
|
|
|
|
|
|
return $drh; |
1179
|
|
|
|
|
|
|
} |
1180
|
|
|
|
|
|
|
sub CLONE { |
1181
|
|
|
|
|
|
|
undef $drh; |
1182
|
|
|
|
|
|
|
} |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
sub FETCH { |
1185
|
|
|
|
|
|
|
my($drh, $key) = @_; |
1186
|
|
|
|
|
|
|
return DBI->trace if $key eq 'DebugDispatch'; |
1187
|
|
|
|
|
|
|
return undef if $key eq 'DebugLog'; # not worth fetching, sorry |
1188
|
|
|
|
|
|
|
return $drh->DBD::_::dr::FETCH($key); |
1189
|
|
|
|
|
|
|
undef; |
1190
|
|
|
|
|
|
|
} |
1191
|
|
|
|
|
|
|
sub STORE { |
1192
|
|
|
|
|
|
|
my($drh, $key, $value) = @_; |
1193
|
|
|
|
|
|
|
if ($key eq 'DebugDispatch') { |
1194
|
|
|
|
|
|
|
DBI->trace($value); |
1195
|
|
|
|
|
|
|
} elsif ($key eq 'DebugLog') { |
1196
|
|
|
|
|
|
|
DBI->trace(-1, $value); |
1197
|
|
|
|
|
|
|
} else { |
1198
|
|
|
|
|
|
|
$drh->DBD::_::dr::STORE($key, $value); |
1199
|
|
|
|
|
|
|
} |
1200
|
|
|
|
|
|
|
} |
1201
|
|
|
|
|
|
|
} |
1202
|
|
|
|
|
|
|
|
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
# -------------------------------------------------------------------- |
1205
|
|
|
|
|
|
|
# === OPTIONAL MINIMAL BASE CLASSES FOR DBI SUBCLASSES === |
1206
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
# We only define default methods for harmless functions. |
1208
|
|
|
|
|
|
|
# We don't, for example, define a DBD::_::st::prepare() |
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
{ package # hide from PAUSE |
1211
|
|
|
|
|
|
|
DBD::_::common; # ====== Common base class methods ====== |
1212
|
|
|
|
|
|
|
use strict; |
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
# methods common to all handle types: |
1215
|
|
|
|
|
|
|
|
1216
|
|
|
|
|
|
|
sub _not_impl { |
1217
|
|
|
|
|
|
|
my ($h, $method) = @_; |
1218
|
|
|
|
|
|
|
$h->trace_msg("Driver does not implement the $method method.\n"); |
1219
|
|
|
|
|
|
|
return; # empty list / undef |
1220
|
|
|
|
|
|
|
} |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
# generic TIEHASH default methods: |
1223
|
|
|
|
|
|
|
sub FIRSTKEY { } |
1224
|
|
|
|
|
|
|
sub NEXTKEY { } |
1225
|
|
|
|
|
|
|
sub EXISTS { defined($_[0]->FETCH($_[1])) } # XXX undef? |
1226
|
|
|
|
|
|
|
sub CLEAR { Carp::carp "Can't CLEAR $_[0] (DBI)" } |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
sub FETCH_many { # XXX should move to C one day |
1229
|
|
|
|
|
|
|
my $h = shift; |
1230
|
|
|
|
|
|
|
# scalar is needed to workaround drivers that return an empty list |
1231
|
|
|
|
|
|
|
# for some attributes |
1232
|
|
|
|
|
|
|
return map { scalar $h->FETCH($_) } @_; |
1233
|
|
|
|
|
|
|
} |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
*dump_handle = \&DBI::dump_handle; |
1236
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
sub install_method { |
1238
|
|
|
|
|
|
|
# special class method called directly by apps and/or drivers |
1239
|
|
|
|
|
|
|
# to install new methods into the DBI dispatcher |
1240
|
|
|
|
|
|
|
# DBD::Foo::db->install_method("foo_mumble", { usage => [...], options => '...' }); |
1241
|
|
|
|
|
|
|
my ($class, $method, $attr) = @_; |
1242
|
|
|
|
|
|
|
Carp::croak("Class '$class' must begin with DBD:: and end with ::db or ::st") |
1243
|
|
|
|
|
|
|
unless $class =~ /^DBD::(\w+)::(dr|db|st)$/; |
1244
|
|
|
|
|
|
|
my ($driver, $subtype) = ($1, $2); |
1245
|
|
|
|
|
|
|
Carp::croak("invalid method name '$method'") |
1246
|
|
|
|
|
|
|
unless $method =~ m/^([a-z]+_)\w+$/; |
1247
|
|
|
|
|
|
|
my $prefix = $1; |
1248
|
|
|
|
|
|
|
my $reg_info = $dbd_prefix_registry->{$prefix}; |
1249
|
|
|
|
|
|
|
Carp::carp("method name prefix '$prefix' is not associated with a registered driver") unless $reg_info; |
1250
|
|
|
|
|
|
|
|
1251
|
|
|
|
|
|
|
my $full_method = "DBI::${subtype}::$method"; |
1252
|
|
|
|
|
|
|
$DBI::installed_methods{$full_method} = $attr; |
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
my (undef, $filename, $line) = caller; |
1255
|
|
|
|
|
|
|
# XXX reformat $attr as needed for _install_method |
1256
|
|
|
|
|
|
|
my %attr = %{$attr||{}}; # copy so we can edit |
1257
|
|
|
|
|
|
|
DBI->_install_method("DBI::${subtype}::$method", "$filename at line $line", \%attr); |
1258
|
|
|
|
|
|
|
} |
1259
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
sub parse_trace_flags { |
1261
|
|
|
|
|
|
|
my ($h, $spec) = @_; |
1262
|
|
|
|
|
|
|
my $level = 0; |
1263
|
|
|
|
|
|
|
my $flags = 0; |
1264
|
|
|
|
|
|
|
my @unknown; |
1265
|
|
|
|
|
|
|
for my $word (split /\s*[|&,]\s*/, $spec) { |
1266
|
|
|
|
|
|
|
if (DBI::looks_like_number($word) && $word <= 0xF && $word >= 0) { |
1267
|
|
|
|
|
|
|
$level = $word; |
1268
|
|
|
|
|
|
|
} elsif ($word eq 'ALL') { |
1269
|
|
|
|
|
|
|
$flags = 0x7FFFFFFF; # XXX last bit causes negative headaches |
1270
|
|
|
|
|
|
|
last; |
1271
|
|
|
|
|
|
|
} elsif (my $flag = $h->parse_trace_flag($word)) { |
1272
|
|
|
|
|
|
|
$flags |= $flag; |
1273
|
|
|
|
|
|
|
} |
1274
|
|
|
|
|
|
|
else { |
1275
|
|
|
|
|
|
|
push @unknown, $word; |
1276
|
|
|
|
|
|
|
} |
1277
|
|
|
|
|
|
|
} |
1278
|
|
|
|
|
|
|
if (@unknown && (ref $h ? $h->FETCH('Warn') : 1)) { |
1279
|
|
|
|
|
|
|
Carp::carp("$h->parse_trace_flags($spec) ignored unknown trace flags: ". |
1280
|
|
|
|
|
|
|
join(" ", map { DBI::neat($_) } @unknown)); |
1281
|
|
|
|
|
|
|
} |
1282
|
|
|
|
|
|
|
$flags |= $level; |
1283
|
|
|
|
|
|
|
return $flags; |
1284
|
|
|
|
|
|
|
} |
1285
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
sub parse_trace_flag { |
1287
|
|
|
|
|
|
|
my ($h, $name) = @_; |
1288
|
|
|
|
|
|
|
# 0xddDDDDrL (driver, DBI, reserved, Level) |
1289
|
|
|
|
|
|
|
return 0x00000100 if $name eq 'SQL'; |
1290
|
|
|
|
|
|
|
return; |
1291
|
|
|
|
|
|
|
} |
1292
|
|
|
|
|
|
|
|
1293
|
|
|
|
|
|
|
sub private_attribute_info { |
1294
|
|
|
|
|
|
|
return undef; |
1295
|
|
|
|
|
|
|
} |
1296
|
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
sub visit_child_handles { |
1298
|
|
|
|
|
|
|
my ($h, $code, $info) = @_; |
1299
|
|
|
|
|
|
|
$info = {} if not defined $info; |
1300
|
|
|
|
|
|
|
for my $ch (@{ $h->{ChildHandles} || []}) { |
1301
|
|
|
|
|
|
|
next unless $ch; |
1302
|
|
|
|
|
|
|
my $child_info = $code->($ch, $info) |
1303
|
|
|
|
|
|
|
or next; |
1304
|
|
|
|
|
|
|
$ch->visit_child_handles($code, $child_info); |
1305
|
|
|
|
|
|
|
} |
1306
|
|
|
|
|
|
|
return $info; |
1307
|
|
|
|
|
|
|
} |
1308
|
|
|
|
|
|
|
} |
1309
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
|
1311
|
|
|
|
|
|
|
{ package # hide from PAUSE |
1312
|
|
|
|
|
|
|
DBD::_::dr; # ====== DRIVER ====== |
1313
|
|
|
|
|
|
|
@DBD::_::dr::ISA = qw(DBD::_::common); |
1314
|
|
|
|
|
|
|
use strict; |
1315
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
sub default_user { |
1317
|
|
|
|
|
|
|
my ($drh, $user, $pass, $attr) = @_; |
1318
|
|
|
|
|
|
|
$user = $ENV{DBI_USER} unless defined $user; |
1319
|
|
|
|
|
|
|
$pass = $ENV{DBI_PASS} unless defined $pass; |
1320
|
|
|
|
|
|
|
return ($user, $pass); |
1321
|
|
|
|
|
|
|
} |
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
sub connect { # normally overridden, but a handy default |
1324
|
|
|
|
|
|
|
my ($drh, $dsn, $user, $auth) = @_; |
1325
|
|
|
|
|
|
|
my ($this) = DBI::_new_dbh($drh, { |
1326
|
|
|
|
|
|
|
'Name' => $dsn, |
1327
|
|
|
|
|
|
|
}); |
1328
|
|
|
|
|
|
|
# XXX debatable as there's no "server side" here |
1329
|
|
|
|
|
|
|
# (and now many uses would trigger warnings on DESTROY) |
1330
|
|
|
|
|
|
|
# $this->STORE(Active => 1); |
1331
|
|
|
|
|
|
|
# so drivers should set it in their own connect |
1332
|
|
|
|
|
|
|
$this; |
1333
|
|
|
|
|
|
|
} |
1334
|
|
|
|
|
|
|
|
1335
|
|
|
|
|
|
|
|
1336
|
|
|
|
|
|
|
sub connect_cached { |
1337
|
|
|
|
|
|
|
my $drh = shift; |
1338
|
|
|
|
|
|
|
my ($dsn, $user, $auth, $attr) = @_; |
1339
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
my $cache = $drh->{CachedKids} ||= {}; |
1341
|
|
|
|
|
|
|
my $key = do { local $^W; |
1342
|
|
|
|
|
|
|
join "!\001", $dsn, $user, $auth, DBI::_concat_hash_sorted($attr, "=\001", ",\001", 0, 0) |
1343
|
|
|
|
|
|
|
}; |
1344
|
|
|
|
|
|
|
my $dbh = $cache->{$key}; |
1345
|
|
|
|
|
|
|
$drh->trace_msg(sprintf(" connect_cached: key '$key', cached dbh $dbh\n", DBI::neat($key), DBI::neat($dbh))) |
1346
|
|
|
|
|
|
|
if $DBI::dbi_debug >= 4; |
1347
|
|
|
|
|
|
|
|
1348
|
|
|
|
|
|
|
my $cb = $attr->{Callbacks}; # take care not to autovivify |
1349
|
|
|
|
|
|
|
if ($dbh && $dbh->FETCH('Active') && eval { $dbh->ping }) { |
1350
|
|
|
|
|
|
|
# If the caller has provided a callback then call it |
1351
|
|
|
|
|
|
|
if ($cb and $cb = $cb->{"connect_cached.reused"}) { |
1352
|
|
|
|
|
|
|
local $_ = "connect_cached.reused"; |
1353
|
|
|
|
|
|
|
$cb->($dbh, $dsn, $user, $auth, $attr); |
1354
|
|
|
|
|
|
|
} |
1355
|
|
|
|
|
|
|
return $dbh; |
1356
|
|
|
|
|
|
|
} |
1357
|
|
|
|
|
|
|
|
1358
|
|
|
|
|
|
|
# If the caller has provided a callback then call it |
1359
|
|
|
|
|
|
|
if ($cb and $cb = $cb->{"connect_cached.new"}) { |
1360
|
|
|
|
|
|
|
local $_ = "connect_cached.new"; |
1361
|
|
|
|
|
|
|
$cb->($dbh, $dsn, $user, $auth, $attr); |
1362
|
|
|
|
|
|
|
} |
1363
|
|
|
|
|
|
|
|
1364
|
|
|
|
|
|
|
$dbh = $drh->connect(@_); |
1365
|
|
|
|
|
|
|
$cache->{$key} = $dbh; # replace prev entry, even if connect failed |
1366
|
|
|
|
|
|
|
return $dbh; |
1367
|
|
|
|
|
|
|
} |
1368
|
|
|
|
|
|
|
|
1369
|
|
|
|
|
|
|
} |
1370
|
|
|
|
|
|
|
|
1371
|
|
|
|
|
|
|
|
1372
|
|
|
|
|
|
|
{ package # hide from PAUSE |
1373
|
|
|
|
|
|
|
DBD::_::db; # ====== DATABASE ====== |
1374
|
|
|
|
|
|
|
@DBD::_::db::ISA = qw(DBD::_::common); |
1375
|
|
|
|
|
|
|
use strict; |
1376
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
sub clone { |
1378
|
|
|
|
|
|
|
my ($old_dbh, $attr) = @_; |
1379
|
|
|
|
|
|
|
my $closure = $old_dbh->{dbi_connect_closure} or return; |
1380
|
|
|
|
|
|
|
unless ($attr) { |
1381
|
|
|
|
|
|
|
# copy attributes visible in the attribute cache |
1382
|
|
|
|
|
|
|
keys %$old_dbh; # reset iterator |
1383
|
|
|
|
|
|
|
while ( my ($k, $v) = each %$old_dbh ) { |
1384
|
|
|
|
|
|
|
# ignore non-code refs, i.e., caches, handles, Err etc |
1385
|
|
|
|
|
|
|
next if ref $v && ref $v ne 'CODE'; # HandleError etc |
1386
|
|
|
|
|
|
|
$attr->{$k} = $v; |
1387
|
|
|
|
|
|
|
} |
1388
|
|
|
|
|
|
|
# explicitly set attributes which are unlikely to be in the |
1389
|
|
|
|
|
|
|
# attribute cache, i.e., boolean's and some others |
1390
|
|
|
|
|
|
|
$attr->{$_} = $old_dbh->FETCH($_) for (qw( |
1391
|
|
|
|
|
|
|
AutoCommit ChopBlanks InactiveDestroy AutoInactiveDestroy |
1392
|
|
|
|
|
|
|
LongTruncOk PrintError PrintWarn Profile RaiseError |
1393
|
|
|
|
|
|
|
ShowErrorStatement TaintIn TaintOut |
1394
|
|
|
|
|
|
|
)); |
1395
|
|
|
|
|
|
|
} |
1396
|
|
|
|
|
|
|
# use Data::Dumper; warn Dumper([$old_dbh, $attr]); |
1397
|
|
|
|
|
|
|
my $new_dbh = &$closure($old_dbh, $attr); |
1398
|
|
|
|
|
|
|
unless ($new_dbh) { |
1399
|
|
|
|
|
|
|
# need to copy err/errstr from driver back into $old_dbh |
1400
|
|
|
|
|
|
|
my $drh = $old_dbh->{Driver}; |
1401
|
|
|
|
|
|
|
return $old_dbh->set_err($drh->err, $drh->errstr, $drh->state); |
1402
|
|
|
|
|
|
|
} |
1403
|
|
|
|
|
|
|
return $new_dbh; |
1404
|
|
|
|
|
|
|
} |
1405
|
|
|
|
|
|
|
|
1406
|
|
|
|
|
|
|
sub quote_identifier { |
1407
|
|
|
|
|
|
|
my ($dbh, @id) = @_; |
1408
|
|
|
|
|
|
|
my $attr = (@id > 3 && ref($id[-1])) ? pop @id : undef; |
1409
|
|
|
|
|
|
|
|
1410
|
|
|
|
|
|
|
my $info = $dbh->{dbi_quote_identifier_cache} ||= [ |
1411
|
|
|
|
|
|
|
$dbh->get_info(29) || '"', # SQL_IDENTIFIER_QUOTE_CHAR |
1412
|
|
|
|
|
|
|
$dbh->get_info(41) || '.', # SQL_CATALOG_NAME_SEPARATOR |
1413
|
|
|
|
|
|
|
$dbh->get_info(114) || 1, # SQL_CATALOG_LOCATION |
1414
|
|
|
|
|
|
|
]; |
1415
|
|
|
|
|
|
|
|
1416
|
|
|
|
|
|
|
my $quote = $info->[0]; |
1417
|
|
|
|
|
|
|
foreach (@id) { # quote the elements |
1418
|
|
|
|
|
|
|
next unless defined; |
1419
|
|
|
|
|
|
|
s/$quote/$quote$quote/g; # escape embedded quotes |
1420
|
|
|
|
|
|
|
$_ = qq{$quote$_$quote}; |
1421
|
|
|
|
|
|
|
} |
1422
|
|
|
|
|
|
|
|
1423
|
|
|
|
|
|
|
# strip out catalog if present for special handling |
1424
|
|
|
|
|
|
|
my $catalog = (@id >= 3) ? shift @id : undef; |
1425
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
# join the dots, ignoring any null/undef elements (ie schema) |
1427
|
|
|
|
|
|
|
my $quoted_id = join '.', grep { defined } @id; |
1428
|
|
|
|
|
|
|
|
1429
|
|
|
|
|
|
|
if ($catalog) { # add catalog correctly |
1430
|
|
|
|
|
|
|
$quoted_id = ($info->[2] == 2) # SQL_CL_END |
1431
|
|
|
|
|
|
|
? $quoted_id . $info->[1] . $catalog |
1432
|
|
|
|
|
|
|
: $catalog . $info->[1] . $quoted_id; |
1433
|
|
|
|
|
|
|
} |
1434
|
|
|
|
|
|
|
return $quoted_id; |
1435
|
|
|
|
|
|
|
} |
1436
|
|
|
|
|
|
|
|
1437
|
|
|
|
|
|
|
sub quote { |
1438
|
|
|
|
|
|
|
my ($dbh, $str, $data_type) = @_; |
1439
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
return "NULL" unless defined $str; |
1441
|
|
|
|
|
|
|
unless ($data_type) { |
1442
|
|
|
|
|
|
|
$str =~ s/'/''/g; # ISO SQL2 |
1443
|
|
|
|
|
|
|
return "'$str'"; |
1444
|
|
|
|
|
|
|
} |
1445
|
|
|
|
|
|
|
|
1446
|
|
|
|
|
|
|
my $dbi_literal_quote_cache = $dbh->{'dbi_literal_quote_cache'} ||= [ {} , {} ]; |
1447
|
|
|
|
|
|
|
my ($prefixes, $suffixes) = @$dbi_literal_quote_cache; |
1448
|
|
|
|
|
|
|
|
1449
|
|
|
|
|
|
|
my $lp = $prefixes->{$data_type}; |
1450
|
|
|
|
|
|
|
my $ls = $suffixes->{$data_type}; |
1451
|
|
|
|
|
|
|
|
1452
|
|
|
|
|
|
|
if ( ! defined $lp || ! defined $ls ) { |
1453
|
|
|
|
|
|
|
my $ti = $dbh->type_info($data_type); |
1454
|
|
|
|
|
|
|
$lp = $prefixes->{$data_type} = $ti ? $ti->{LITERAL_PREFIX} || "" : "'"; |
1455
|
|
|
|
|
|
|
$ls = $suffixes->{$data_type} = $ti ? $ti->{LITERAL_SUFFIX} || "" : "'"; |
1456
|
|
|
|
|
|
|
} |
1457
|
|
|
|
|
|
|
return $str unless $lp || $ls; # no quoting required |
1458
|
|
|
|
|
|
|
|
1459
|
|
|
|
|
|
|
# XXX don't know what the standard says about escaping |
1460
|
|
|
|
|
|
|
# in the 'general case' (where $lp != "'"). |
1461
|
|
|
|
|
|
|
# So we just do this and hope: |
1462
|
|
|
|
|
|
|
$str =~ s/$lp/$lp$lp/g |
1463
|
|
|
|
|
|
|
if $lp && $lp eq $ls && ($lp eq "'" || $lp eq '"'); |
1464
|
|
|
|
|
|
|
return "$lp$str$ls"; |
1465
|
|
|
|
|
|
|
} |
1466
|
|
|
|
|
|
|
|
1467
|
|
|
|
|
|
|
sub rows { -1 } # here so $DBI::rows 'works' after using $dbh |
1468
|
|
|
|
|
|
|
|
1469
|
|
|
|
|
|
|
sub do { |
1470
|
|
|
|
|
|
|
my($dbh, $statement, $attr, @params) = @_; |
1471
|
|
|
|
|
|
|
my $sth = $dbh->prepare($statement, $attr) or return undef; |
1472
|
|
|
|
|
|
|
$sth->execute(@params) or return undef; |
1473
|
|
|
|
|
|
|
my $rows = $sth->rows; |
1474
|
|
|
|
|
|
|
($rows == 0) ? "0E0" : $rows; |
1475
|
|
|
|
|
|
|
} |
1476
|
|
|
|
|
|
|
|
1477
|
|
|
|
|
|
|
sub _do_selectrow { |
1478
|
|
|
|
|
|
|
my ($method, $dbh, $stmt, $attr, @bind) = @_; |
1479
|
|
|
|
|
|
|
my $sth = ((ref $stmt) ? $stmt : $dbh->prepare($stmt, $attr)) |
1480
|
|
|
|
|
|
|
or return; |
1481
|
|
|
|
|
|
|
$sth->execute(@bind) |
1482
|
|
|
|
|
|
|
or return; |
1483
|
|
|
|
|
|
|
my $row = $sth->$method() |
1484
|
|
|
|
|
|
|
and $sth->finish; |
1485
|
|
|
|
|
|
|
return $row; |
1486
|
|
|
|
|
|
|
} |
1487
|
|
|
|
|
|
|
|
1488
|
|
|
|
|
|
|
sub selectrow_hashref { return _do_selectrow('fetchrow_hashref', @_); } |
1489
|
|
|
|
|
|
|
|
1490
|
|
|
|
|
|
|
# XXX selectrow_array/ref also have C implementations in Driver.xst |
1491
|
|
|
|
|
|
|
sub selectrow_arrayref { return _do_selectrow('fetchrow_arrayref', @_); } |
1492
|
|
|
|
|
|
|
sub selectrow_array { |
1493
|
|
|
|
|
|
|
my $row = _do_selectrow('fetchrow_arrayref', @_) or return; |
1494
|
|
|
|
|
|
|
return $row->[0] unless wantarray; |
1495
|
|
|
|
|
|
|
return @$row; |
1496
|
|
|
|
|
|
|
} |
1497
|
|
|
|
|
|
|
|
1498
|
|
|
|
|
|
|
# XXX selectall_arrayref also has C implementation in Driver.xst |
1499
|
|
|
|
|
|
|
# which fallsback to this if a slice is given |
1500
|
|
|
|
|
|
|
sub selectall_arrayref { |
1501
|
|
|
|
|
|
|
my ($dbh, $stmt, $attr, @bind) = @_; |
1502
|
|
|
|
|
|
|
my $sth = (ref $stmt) ? $stmt : $dbh->prepare($stmt, $attr) |
1503
|
|
|
|
|
|
|
or return; |
1504
|
|
|
|
|
|
|
$sth->execute(@bind) || return; |
1505
|
|
|
|
|
|
|
my $slice = $attr->{Slice}; # typically undef, else hash or array ref |
1506
|
|
|
|
|
|
|
if (!$slice and $slice=$attr->{Columns}) { |
1507
|
|
|
|
|
|
|
if (ref $slice eq 'ARRAY') { # map col idx to perl array idx |
1508
|
|
|
|
|
|
|
$slice = [ @{$attr->{Columns}} ]; # take a copy |
1509
|
|
|
|
|
|
|
for (@$slice) { $_-- } |
1510
|
|
|
|
|
|
|
} |
1511
|
|
|
|
|
|
|
} |
1512
|
|
|
|
|
|
|
my $rows = $sth->fetchall_arrayref($slice, my $MaxRows = $attr->{MaxRows}); |
1513
|
|
|
|
|
|
|
$sth->finish if defined $MaxRows; |
1514
|
|
|
|
|
|
|
return $rows; |
1515
|
|
|
|
|
|
|
} |
1516
|
|
|
|
|
|
|
|
1517
|
|
|
|
|
|
|
sub selectall_hashref { |
1518
|
|
|
|
|
|
|
my ($dbh, $stmt, $key_field, $attr, @bind) = @_; |
1519
|
|
|
|
|
|
|
my $sth = (ref $stmt) ? $stmt : $dbh->prepare($stmt, $attr); |
1520
|
|
|
|
|
|
|
return unless $sth; |
1521
|
|
|
|
|
|
|
$sth->execute(@bind) || return; |
1522
|
|
|
|
|
|
|
return $sth->fetchall_hashref($key_field); |
1523
|
|
|
|
|
|
|
} |
1524
|
|
|
|
|
|
|
|
1525
|
|
|
|
|
|
|
sub selectcol_arrayref { |
1526
|
|
|
|
|
|
|
my ($dbh, $stmt, $attr, @bind) = @_; |
1527
|
|
|
|
|
|
|
my $sth = (ref $stmt) ? $stmt : $dbh->prepare($stmt, $attr); |
1528
|
|
|
|
|
|
|
return unless $sth; |
1529
|
|
|
|
|
|
|
$sth->execute(@bind) || return; |
1530
|
|
|
|
|
|
|
my @columns = ($attr->{Columns}) ? @{$attr->{Columns}} : (1); |
1531
|
|
|
|
|
|
|
my @values = (undef) x @columns; |
1532
|
|
|
|
|
|
|
my $idx = 0; |
1533
|
|
|
|
|
|
|
for (@columns) { |
1534
|
|
|
|
|
|
|
$sth->bind_col($_, \$values[$idx++]) || return; |
1535
|
|
|
|
|
|
|
} |
1536
|
|
|
|
|
|
|
my @col; |
1537
|
|
|
|
|
|
|
if (my $max = $attr->{MaxRows}) { |
1538
|
|
|
|
|
|
|
push @col, @values while 0 < $max-- && $sth->fetch; |
1539
|
|
|
|
|
|
|
} |
1540
|
|
|
|
|
|
|
else { |
1541
|
|
|
|
|
|
|
push @col, @values while $sth->fetch; |
1542
|
|
|
|
|
|
|
} |
1543
|
|
|
|
|
|
|
return \@col; |
1544
|
|
|
|
|
|
|
} |
1545
|
|
|
|
|
|
|
|
1546
|
|
|
|
|
|
|
sub prepare_cached { |
1547
|
|
|
|
|
|
|
my ($dbh, $statement, $attr, $if_active) = @_; |
1548
|
|
|
|
|
|
|
|
1549
|
|
|
|
|
|
|
# Needs support at dbh level to clear cache before complaining about |
1550
|
|
|
|
|
|
|
# active children. The XS template code does this. Drivers not using |
1551
|
|
|
|
|
|
|
# the template must handle clearing the cache themselves. |
1552
|
|
|
|
|
|
|
my $cache = $dbh->{CachedKids} ||= {}; |
1553
|
|
|
|
|
|
|
my $key = do { local $^W; |
1554
|
|
|
|
|
|
|
join "!\001", $statement, DBI::_concat_hash_sorted($attr, "=\001", ",\001", 0, 0) |
1555
|
|
|
|
|
|
|
}; |
1556
|
|
|
|
|
|
|
my $sth = $cache->{$key}; |
1557
|
|
|
|
|
|
|
|
1558
|
|
|
|
|
|
|
if ($sth) { |
1559
|
|
|
|
|
|
|
return $sth unless $sth->FETCH('Active'); |
1560
|
|
|
|
|
|
|
Carp::carp("prepare_cached($statement) statement handle $sth still Active") |
1561
|
|
|
|
|
|
|
unless ($if_active ||= 0); |
1562
|
|
|
|
|
|
|
$sth->finish if $if_active <= 1; |
1563
|
|
|
|
|
|
|
return $sth if $if_active <= 2; |
1564
|
|
|
|
|
|
|
} |
1565
|
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
$sth = $dbh->prepare($statement, $attr); |
1567
|
|
|
|
|
|
|
$cache->{$key} = $sth if $sth; |
1568
|
|
|
|
|
|
|
|
1569
|
|
|
|
|
|
|
return $sth; |
1570
|
|
|
|
|
|
|
} |
1571
|
|
|
|
|
|
|
|
1572
|
|
|
|
|
|
|
sub ping { |
1573
|
|
|
|
|
|
|
my $dbh = shift; |
1574
|
|
|
|
|
|
|
$dbh->_not_impl('ping'); |
1575
|
|
|
|
|
|
|
# "0 but true" is a special kind of true 0 that is used here so |
1576
|
|
|
|
|
|
|
# applications can check if the ping was a real ping or not |
1577
|
|
|
|
|
|
|
($dbh->FETCH('Active')) ? "0 but true" : 0; |
1578
|
|
|
|
|
|
|
} |
1579
|
|
|
|
|
|
|
|
1580
|
|
|
|
|
|
|
sub begin_work { |
1581
|
|
|
|
|
|
|
my $dbh = shift; |
1582
|
|
|
|
|
|
|
return $dbh->set_err($DBI::stderr, "Already in a transaction") |
1583
|
|
|
|
|
|
|
unless $dbh->FETCH('AutoCommit'); |
1584
|
|
|
|
|
|
|
$dbh->STORE('AutoCommit', 0); # will croak if driver doesn't support it |
1585
|
|
|
|
|
|
|
$dbh->STORE('BegunWork', 1); # trigger post commit/rollback action |
1586
|
|
|
|
|
|
|
return 1; |
1587
|
|
|
|
|
|
|
} |
1588
|
|
|
|
|
|
|
|
1589
|
|
|
|
|
|
|
sub primary_key { |
1590
|
|
|
|
|
|
|
my ($dbh, @args) = @_; |
1591
|
|
|
|
|
|
|
my $sth = $dbh->primary_key_info(@args) or return; |
1592
|
|
|
|
|
|
|
my ($row, @col); |
1593
|
|
|
|
|
|
|
push @col, $row->[3] while ($row = $sth->fetch); |
1594
|
|
|
|
|
|
|
Carp::croak("primary_key method not called in list context") |
1595
|
|
|
|
|
|
|
unless wantarray; # leave us some elbow room |
1596
|
|
|
|
|
|
|
return @col; |
1597
|
|
|
|
|
|
|
} |
1598
|
|
|
|
|
|
|
|
1599
|
|
|
|
|
|
|
sub tables { |
1600
|
|
|
|
|
|
|
my ($dbh, @args) = @_; |
1601
|
|
|
|
|
|
|
my $sth = $dbh->table_info(@args[0,1,2,3,4]) or return; |
1602
|
|
|
|
|
|
|
my $tables = $sth->fetchall_arrayref or return; |
1603
|
|
|
|
|
|
|
my @tables; |
1604
|
|
|
|
|
|
|
if ($dbh->get_info(29)) { # SQL_IDENTIFIER_QUOTE_CHAR |
1605
|
|
|
|
|
|
|
@tables = map { $dbh->quote_identifier( @{$_}[0,1,2] ) } @$tables; |
1606
|
|
|
|
|
|
|
} |
1607
|
|
|
|
|
|
|
else { # temporary old style hack (yeach) |
1608
|
|
|
|
|
|
|
@tables = map { |
1609
|
|
|
|
|
|
|
my $name = $_->[2]; |
1610
|
|
|
|
|
|
|
if ($_->[1]) { |
1611
|
|
|
|
|
|
|
my $schema = $_->[1]; |
1612
|
|
|
|
|
|
|
# a sad hack (mostly for Informix I recall) |
1613
|
|
|
|
|
|
|
my $quote = ($schema eq uc($schema)) ? '' : '"'; |
1614
|
|
|
|
|
|
|
$name = "$quote$schema$quote.$name" |
1615
|
|
|
|
|
|
|
} |
1616
|
|
|
|
|
|
|
$name; |
1617
|
|
|
|
|
|
|
} @$tables; |
1618
|
|
|
|
|
|
|
} |
1619
|
|
|
|
|
|
|
return @tables; |
1620
|
|
|
|
|
|
|
} |
1621
|
|
|
|
|
|
|
|
1622
|
|
|
|
|
|
|
sub type_info { # this should be sufficient for all drivers |
1623
|
|
|
|
|
|
|
my ($dbh, $data_type) = @_; |
1624
|
|
|
|
|
|
|
my $idx_hash; |
1625
|
|
|
|
|
|
|
my $tia = $dbh->{dbi_type_info_row_cache}; |
1626
|
|
|
|
|
|
|
if ($tia) { |
1627
|
|
|
|
|
|
|
$idx_hash = $dbh->{dbi_type_info_idx_cache}; |
1628
|
|
|
|
|
|
|
} |
1629
|
|
|
|
|
|
|
else { |
1630
|
|
|
|
|
|
|
my $temp = $dbh->type_info_all; |
1631
|
|
|
|
|
|
|
return unless $temp && @$temp; |
1632
|
|
|
|
|
|
|
# we cache here because type_info_all may be expensive to call |
1633
|
|
|
|
|
|
|
# (and we take a copy so the following shift can't corrupt |
1634
|
|
|
|
|
|
|
# the data that may be returned by future calls to type_info_all) |
1635
|
|
|
|
|
|
|
$tia = $dbh->{dbi_type_info_row_cache} = [ @$temp ]; |
1636
|
|
|
|
|
|
|
$idx_hash = $dbh->{dbi_type_info_idx_cache} = shift @$tia; |
1637
|
|
|
|
|
|
|
} |
1638
|
|
|
|
|
|
|
|
1639
|
|
|
|
|
|
|
my $dt_idx = $idx_hash->{DATA_TYPE} || $idx_hash->{data_type}; |
1640
|
|
|
|
|
|
|
Carp::croak("type_info_all returned non-standard DATA_TYPE index value ($dt_idx != 1)") |
1641
|
|
|
|
|
|
|
if $dt_idx && $dt_idx != 1; |
1642
|
|
|
|
|
|
|
|
1643
|
|
|
|
|
|
|
# --- simple DATA_TYPE match filter |
1644
|
|
|
|
|
|
|
my @ti; |
1645
|
|
|
|
|
|
|
my @data_type_list = (ref $data_type) ? @$data_type : ($data_type); |
1646
|
|
|
|
|
|
|
foreach $data_type (@data_type_list) { |
1647
|
|
|
|
|
|
|
if (defined($data_type) && $data_type != DBI::SQL_ALL_TYPES()) { |
1648
|
|
|
|
|
|
|
push @ti, grep { $_->[$dt_idx] == $data_type } @$tia; |
1649
|
|
|
|
|
|
|
} |
1650
|
|
|
|
|
|
|
else { # SQL_ALL_TYPES |
1651
|
|
|
|
|
|
|
push @ti, @$tia; |
1652
|
|
|
|
|
|
|
} |
1653
|
|
|
|
|
|
|
last if @ti; # found at least one match |
1654
|
|
|
|
|
|
|
} |
1655
|
|
|
|
|
|
|
|
1656
|
|
|
|
|
|
|
# --- format results into list of hash refs |
1657
|
|
|
|
|
|
|
my $idx_fields = keys %$idx_hash; |
1658
|
|
|
|
|
|
|
my @idx_names = map { uc($_) } keys %$idx_hash; |
1659
|
|
|
|
|
|
|
my @idx_values = values %$idx_hash; |
1660
|
|
|
|
|
|
|
Carp::croak "type_info_all result has $idx_fields keys but ".(@{$ti[0]})." fields" |
1661
|
|
|
|
|
|
|
if @ti && @{$ti[0]} != $idx_fields; |
1662
|
|
|
|
|
|
|
my @out = map { |
1663
|
|
|
|
|
|
|
my %h; @h{@idx_names} = @{$_}[ @idx_values ]; \%h; |
1664
|
|
|
|
|
|
|
} @ti; |
1665
|
|
|
|
|
|
|
return $out[0] unless wantarray; |
1666
|
|
|
|
|
|
|
return @out; |
1667
|
|
|
|
|
|
|
} |
1668
|
|
|
|
|
|
|
|
1669
|
|
|
|
|
|
|
sub data_sources { |
1670
|
|
|
|
|
|
|
my ($dbh, @other) = @_; |
1671
|
|
|
|
|
|
|
my $drh = $dbh->{Driver}; # XXX proxy issues? |
1672
|
|
|
|
|
|
|
return $drh->data_sources(@other); |
1673
|
|
|
|
|
|
|
} |
1674
|
|
|
|
|
|
|
|
1675
|
|
|
|
|
|
|
} |
1676
|
|
|
|
|
|
|
|
1677
|
|
|
|
|
|
|
|
1678
|
|
|
|
|
|
|
{ package # hide from PAUSE |
1679
|
|
|
|
|
|
|
DBD::_::st; # ====== STATEMENT ====== |
1680
|
|
|
|
|
|
|
@DBD::_::st::ISA = qw(DBD::_::common); |
1681
|
|
|
|
|
|
|
use strict; |
1682
|
|
|
|
|
|
|
|
1683
|
|
|
|
|
|
|
sub bind_param { Carp::croak("Can't bind_param, not implement by driver") } |
1684
|
|
|
|
|
|
|
|
1685
|
|
|
|
|
|
|
# |
1686
|
|
|
|
|
|
|
# ******************************************************** |
1687
|
|
|
|
|
|
|
# |
1688
|
|
|
|
|
|
|
# BEGIN ARRAY BINDING |
1689
|
|
|
|
|
|
|
# |
1690
|
|
|
|
|
|
|
# Array binding support for drivers which don't support |
1691
|
|
|
|
|
|
|
# array binding, but have sufficient interfaces to fake it. |
1692
|
|
|
|
|
|
|
# NOTE: mixing scalars and arrayrefs requires using bind_param_array |
1693
|
|
|
|
|
|
|
# for *all* params...unless we modify bind_param for the default |
1694
|
|
|
|
|
|
|
# case... |
1695
|
|
|
|
|
|
|
# |
1696
|
|
|
|
|
|
|
# 2002-Apr-10 D. Arnold |
1697
|
|
|
|
|
|
|
|
1698
|
|
|
|
|
|
|
sub bind_param_array { |
1699
|
|
|
|
|
|
|
my $sth = shift; |
1700
|
|
|
|
|
|
|
my ($p_id, $value_array, $attr) = @_; |
1701
|
|
|
|
|
|
|
|
1702
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, "Value for parameter $p_id must be a scalar or an arrayref, not a ".ref($value_array)) |
1703
|
|
|
|
|
|
|
if defined $value_array and ref $value_array and ref $value_array ne 'ARRAY'; |
1704
|
|
|
|
|
|
|
|
1705
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, "Can't use named placeholder '$p_id' for non-driver supported bind_param_array") |
1706
|
|
|
|
|
|
|
unless DBI::looks_like_number($p_id); # because we rely on execute(@ary) here |
1707
|
|
|
|
|
|
|
|
1708
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, "Placeholder '$p_id' is out of range") |
1709
|
|
|
|
|
|
|
if $p_id <= 0; # can't easily/reliably test for too big |
1710
|
|
|
|
|
|
|
|
1711
|
|
|
|
|
|
|
# get/create arrayref to hold params |
1712
|
|
|
|
|
|
|
my $hash_of_arrays = $sth->{ParamArrays} ||= { }; |
1713
|
|
|
|
|
|
|
|
1714
|
|
|
|
|
|
|
# If the bind has attribs then we rely on the driver conforming to |
1715
|
|
|
|
|
|
|
# the DBI spec in that a single bind_param() call with those attribs |
1716
|
|
|
|
|
|
|
# makes them 'sticky' and apply to all later execute(@values) calls. |
1717
|
|
|
|
|
|
|
# Since we only call bind_param() if we're given attribs then |
1718
|
|
|
|
|
|
|
# applications using drivers that don't support bind_param can still |
1719
|
|
|
|
|
|
|
# use bind_param_array() so long as they don't pass any attribs. |
1720
|
|
|
|
|
|
|
|
1721
|
|
|
|
|
|
|
$$hash_of_arrays{$p_id} = $value_array; |
1722
|
|
|
|
|
|
|
return $sth->bind_param($p_id, undef, $attr) |
1723
|
|
|
|
|
|
|
if $attr; |
1724
|
|
|
|
|
|
|
1; |
1725
|
|
|
|
|
|
|
} |
1726
|
|
|
|
|
|
|
|
1727
|
|
|
|
|
|
|
sub bind_param_inout_array { |
1728
|
|
|
|
|
|
|
my $sth = shift; |
1729
|
|
|
|
|
|
|
# XXX not supported so we just call bind_param_array instead |
1730
|
|
|
|
|
|
|
# and then return an error |
1731
|
|
|
|
|
|
|
my ($p_num, $value_array, $attr) = @_; |
1732
|
|
|
|
|
|
|
$sth->bind_param_array($p_num, $value_array, $attr); |
1733
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, "bind_param_inout_array not supported"); |
1734
|
|
|
|
|
|
|
} |
1735
|
|
|
|
|
|
|
|
1736
|
|
|
|
|
|
|
sub bind_columns { |
1737
|
|
|
|
|
|
|
my $sth = shift; |
1738
|
|
|
|
|
|
|
my $fields = $sth->FETCH('NUM_OF_FIELDS') || 0; |
1739
|
|
|
|
|
|
|
if ($fields <= 0 && !$sth->{Active}) { |
1740
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, "Statement has no result columns to bind" |
1741
|
|
|
|
|
|
|
." (perhaps you need to successfully call execute first)"); |
1742
|
|
|
|
|
|
|
} |
1743
|
|
|
|
|
|
|
# Backwards compatibility for old-style call with attribute hash |
1744
|
|
|
|
|
|
|
# ref as first arg. Skip arg if undef or a hash ref. |
1745
|
|
|
|
|
|
|
my $attr; |
1746
|
|
|
|
|
|
|
$attr = shift if !defined $_[0] or ref($_[0]) eq 'HASH'; |
1747
|
|
|
|
|
|
|
|
1748
|
|
|
|
|
|
|
my $idx = 0; |
1749
|
|
|
|
|
|
|
$sth->bind_col(++$idx, shift, $attr) or return |
1750
|
|
|
|
|
|
|
while (@_ and $idx < $fields); |
1751
|
|
|
|
|
|
|
|
1752
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, "bind_columns called with ".($idx+@_)." values but $fields are needed") |
1753
|
|
|
|
|
|
|
if @_ or $idx != $fields; |
1754
|
|
|
|
|
|
|
|
1755
|
|
|
|
|
|
|
return 1; |
1756
|
|
|
|
|
|
|
} |
1757
|
|
|
|
|
|
|
|
1758
|
|
|
|
|
|
|
sub execute_array { |
1759
|
|
|
|
|
|
|
my $sth = shift; |
1760
|
|
|
|
|
|
|
my ($attr, @array_of_arrays) = @_; |
1761
|
|
|
|
|
|
|
my $NUM_OF_PARAMS = $sth->FETCH('NUM_OF_PARAMS'); # may be undef at this point |
1762
|
|
|
|
|
|
|
|
1763
|
|
|
|
|
|
|
# get tuple status array or hash attribute |
1764
|
|
|
|
|
|
|
my $tuple_sts = $attr->{ArrayTupleStatus}; |
1765
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, "ArrayTupleStatus attribute must be an arrayref") |
1766
|
|
|
|
|
|
|
if $tuple_sts and ref $tuple_sts ne 'ARRAY'; |
1767
|
|
|
|
|
|
|
|
1768
|
|
|
|
|
|
|
# bind all supplied arrays |
1769
|
|
|
|
|
|
|
if (@array_of_arrays) { |
1770
|
|
|
|
|
|
|
$sth->{ParamArrays} = { }; # clear out old params |
1771
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, |
1772
|
|
|
|
|
|
|
@array_of_arrays." bind values supplied but $NUM_OF_PARAMS expected") |
1773
|
|
|
|
|
|
|
if defined ($NUM_OF_PARAMS) && @array_of_arrays != $NUM_OF_PARAMS; |
1774
|
|
|
|
|
|
|
$sth->bind_param_array($_, $array_of_arrays[$_-1]) or return |
1775
|
|
|
|
|
|
|
foreach (1..@array_of_arrays); |
1776
|
|
|
|
|
|
|
} |
1777
|
|
|
|
|
|
|
|
1778
|
|
|
|
|
|
|
my $fetch_tuple_sub; |
1779
|
|
|
|
|
|
|
|
1780
|
|
|
|
|
|
|
if ($fetch_tuple_sub = $attr->{ArrayTupleFetch}) { # fetch on demand |
1781
|
|
|
|
|
|
|
|
1782
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, |
1783
|
|
|
|
|
|
|
"Can't use both ArrayTupleFetch and explicit bind values") |
1784
|
|
|
|
|
|
|
if @array_of_arrays; # previous bind_param_array calls will simply be ignored |
1785
|
|
|
|
|
|
|
|
1786
|
|
|
|
|
|
|
if (UNIVERSAL::isa($fetch_tuple_sub,'DBI::st')) { |
1787
|
|
|
|
|
|
|
my $fetch_sth = $fetch_tuple_sub; |
1788
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, |
1789
|
|
|
|
|
|
|
"ArrayTupleFetch sth is not Active, need to execute() it first") |
1790
|
|
|
|
|
|
|
unless $fetch_sth->{Active}; |
1791
|
|
|
|
|
|
|
# check column count match to give more friendly message |
1792
|
|
|
|
|
|
|
my $NUM_OF_FIELDS = $fetch_sth->{NUM_OF_FIELDS}; |
1793
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, |
1794
|
|
|
|
|
|
|
"$NUM_OF_FIELDS columns from ArrayTupleFetch sth but $NUM_OF_PARAMS expected") |
1795
|
|
|
|
|
|
|
if defined($NUM_OF_FIELDS) && defined($NUM_OF_PARAMS) |
1796
|
|
|
|
|
|
|
&& $NUM_OF_FIELDS != $NUM_OF_PARAMS; |
1797
|
|
|
|
|
|
|
$fetch_tuple_sub = sub { $fetch_sth->fetchrow_arrayref }; |
1798
|
|
|
|
|
|
|
} |
1799
|
|
|
|
|
|
|
elsif (!UNIVERSAL::isa($fetch_tuple_sub,'CODE')) { |
1800
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, "ArrayTupleFetch '$fetch_tuple_sub' is not a code ref or statement handle"); |
1801
|
|
|
|
|
|
|
} |
1802
|
|
|
|
|
|
|
|
1803
|
|
|
|
|
|
|
} |
1804
|
|
|
|
|
|
|
else { |
1805
|
|
|
|
|
|
|
my $NUM_OF_PARAMS_given = keys %{ $sth->{ParamArrays} || {} }; |
1806
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, |
1807
|
|
|
|
|
|
|
"$NUM_OF_PARAMS_given bind values supplied but $NUM_OF_PARAMS expected") |
1808
|
|
|
|
|
|
|
if defined($NUM_OF_PARAMS) && $NUM_OF_PARAMS != $NUM_OF_PARAMS_given; |
1809
|
|
|
|
|
|
|
|
1810
|
|
|
|
|
|
|
# get the length of a bound array |
1811
|
|
|
|
|
|
|
my $maxlen; |
1812
|
|
|
|
|
|
|
my %hash_of_arrays = %{$sth->{ParamArrays}}; |
1813
|
|
|
|
|
|
|
foreach (keys(%hash_of_arrays)) { |
1814
|
|
|
|
|
|
|
my $ary = $hash_of_arrays{$_}; |
1815
|
|
|
|
|
|
|
next unless ref $ary eq 'ARRAY'; |
1816
|
|
|
|
|
|
|
$maxlen = @$ary if !$maxlen || @$ary > $maxlen; |
1817
|
|
|
|
|
|
|
} |
1818
|
|
|
|
|
|
|
# if there are no arrays then execute scalars once |
1819
|
|
|
|
|
|
|
$maxlen = 1 unless defined $maxlen; |
1820
|
|
|
|
|
|
|
my @bind_ids = 1..keys(%hash_of_arrays); |
1821
|
|
|
|
|
|
|
|
1822
|
|
|
|
|
|
|
my $tuple_idx = 0; |
1823
|
|
|
|
|
|
|
$fetch_tuple_sub = sub { |
1824
|
|
|
|
|
|
|
return if $tuple_idx >= $maxlen; |
1825
|
|
|
|
|
|
|
my @tuple = map { |
1826
|
|
|
|
|
|
|
my $a = $hash_of_arrays{$_}; |
1827
|
|
|
|
|
|
|
ref($a) ? $a->[$tuple_idx] : $a |
1828
|
|
|
|
|
|
|
} @bind_ids; |
1829
|
|
|
|
|
|
|
++$tuple_idx; |
1830
|
|
|
|
|
|
|
return \@tuple; |
1831
|
|
|
|
|
|
|
}; |
1832
|
|
|
|
|
|
|
} |
1833
|
|
|
|
|
|
|
# pass thru the callers scalar or list context |
1834
|
|
|
|
|
|
|
return $sth->execute_for_fetch($fetch_tuple_sub, $tuple_sts); |
1835
|
|
|
|
|
|
|
} |
1836
|
|
|
|
|
|
|
|
1837
|
|
|
|
|
|
|
sub execute_for_fetch { |
1838
|
|
|
|
|
|
|
my ($sth, $fetch_tuple_sub, $tuple_status) = @_; |
1839
|
|
|
|
|
|
|
# start with empty status array |
1840
|
|
|
|
|
|
|
($tuple_status) ? @$tuple_status = () : $tuple_status = []; |
1841
|
|
|
|
|
|
|
|
1842
|
|
|
|
|
|
|
my $rc_total = 0; |
1843
|
|
|
|
|
|
|
my $err_count; |
1844
|
|
|
|
|
|
|
while ( my $tuple = &$fetch_tuple_sub() ) { |
1845
|
|
|
|
|
|
|
if ( my $rc = $sth->execute(@$tuple) ) { |
1846
|
|
|
|
|
|
|
push @$tuple_status, $rc; |
1847
|
|
|
|
|
|
|
$rc_total = ($rc >= 0 && $rc_total >= 0) ? $rc_total + $rc : -1; |
1848
|
|
|
|
|
|
|
} |
1849
|
|
|
|
|
|
|
else { |
1850
|
|
|
|
|
|
|
$err_count++; |
1851
|
|
|
|
|
|
|
push @$tuple_status, [ $sth->err, $sth->errstr, $sth->state ]; |
1852
|
|
|
|
|
|
|
# XXX drivers implementing execute_for_fetch could opt to "last;" here |
1853
|
|
|
|
|
|
|
# if they know the error code means no further executes will work. |
1854
|
|
|
|
|
|
|
} |
1855
|
|
|
|
|
|
|
} |
1856
|
|
|
|
|
|
|
my $tuples = @$tuple_status; |
1857
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, "executing $tuples generated $err_count errors") |
1858
|
|
|
|
|
|
|
if $err_count; |
1859
|
|
|
|
|
|
|
$tuples ||= "0E0"; |
1860
|
|
|
|
|
|
|
return $tuples unless wantarray; |
1861
|
|
|
|
|
|
|
return ($tuples, $rc_total); |
1862
|
|
|
|
|
|
|
} |
1863
|
|
|
|
|
|
|
|
1864
|
|
|
|
|
|
|
|
1865
|
|
|
|
|
|
|
sub fetchall_arrayref { # ALSO IN Driver.xst |
1866
|
|
|
|
|
|
|
my ($sth, $slice, $max_rows) = @_; |
1867
|
|
|
|
|
|
|
|
1868
|
|
|
|
|
|
|
# when batch fetching with $max_rows were very likely to try to |
1869
|
|
|
|
|
|
|
# fetch the 'next batch' after the previous batch returned |
1870
|
|
|
|
|
|
|
# <=$max_rows. So don't treat that as an error. |
1871
|
|
|
|
|
|
|
return undef if $max_rows and not $sth->FETCH('Active'); |
1872
|
|
|
|
|
|
|
|
1873
|
|
|
|
|
|
|
my $mode = ref($slice) || 'ARRAY'; |
1874
|
|
|
|
|
|
|
my @rows; |
1875
|
|
|
|
|
|
|
my $row; |
1876
|
|
|
|
|
|
|
if ($mode eq 'ARRAY') { |
1877
|
|
|
|
|
|
|
# we copy the array here because fetch (currently) always |
1878
|
|
|
|
|
|
|
# returns the same array ref. XXX |
1879
|
|
|
|
|
|
|
if ($slice && @$slice) { |
1880
|
|
|
|
|
|
|
$max_rows = -1 unless defined $max_rows; |
1881
|
|
|
|
|
|
|
push @rows, [ @{$row}[ @$slice] ] |
1882
|
|
|
|
|
|
|
while($max_rows-- and $row = $sth->fetch); |
1883
|
|
|
|
|
|
|
} |
1884
|
|
|
|
|
|
|
elsif (defined $max_rows) { |
1885
|
|
|
|
|
|
|
push @rows, [ @$row ] |
1886
|
|
|
|
|
|
|
while($max_rows-- and $row = $sth->fetch); |
1887
|
|
|
|
|
|
|
} |
1888
|
|
|
|
|
|
|
else { |
1889
|
|
|
|
|
|
|
push @rows, [ @$row ] while($row = $sth->fetch); |
1890
|
|
|
|
|
|
|
} |
1891
|
|
|
|
|
|
|
} |
1892
|
|
|
|
|
|
|
elsif ($mode eq 'HASH') { |
1893
|
|
|
|
|
|
|
$max_rows = -1 unless defined $max_rows; |
1894
|
|
|
|
|
|
|
if (keys %$slice) { |
1895
|
|
|
|
|
|
|
my @o_keys = keys %$slice; |
1896
|
|
|
|
|
|
|
my @i_keys = map { lc } keys %$slice; |
1897
|
|
|
|
|
|
|
# XXX this could be made faster by pre-binding a local hash |
1898
|
|
|
|
|
|
|
# using bind_columns and then copying it per row |
1899
|
|
|
|
|
|
|
while ($max_rows-- and $row = $sth->fetchrow_hashref('NAME_lc')) { |
1900
|
|
|
|
|
|
|
my %hash; |
1901
|
|
|
|
|
|
|
@hash{@o_keys} = @{$row}{@i_keys}; |
1902
|
|
|
|
|
|
|
push @rows, \%hash; |
1903
|
|
|
|
|
|
|
} |
1904
|
|
|
|
|
|
|
} |
1905
|
|
|
|
|
|
|
else { |
1906
|
|
|
|
|
|
|
# XXX assumes new ref each fetchhash |
1907
|
|
|
|
|
|
|
push @rows, $row |
1908
|
|
|
|
|
|
|
while ($max_rows-- and $row = $sth->fetchrow_hashref()); |
1909
|
|
|
|
|
|
|
} |
1910
|
|
|
|
|
|
|
} |
1911
|
|
|
|
|
|
|
else { Carp::croak("fetchall_arrayref($mode) invalid") } |
1912
|
|
|
|
|
|
|
return \@rows; |
1913
|
|
|
|
|
|
|
} |
1914
|
|
|
|
|
|
|
|
1915
|
|
|
|
|
|
|
sub fetchall_hashref { |
1916
|
|
|
|
|
|
|
my ($sth, $key_field) = @_; |
1917
|
|
|
|
|
|
|
|
1918
|
|
|
|
|
|
|
my $hash_key_name = $sth->{FetchHashKeyName} || 'NAME'; |
1919
|
|
|
|
|
|
|
my $names_hash = $sth->FETCH("${hash_key_name}_hash"); |
1920
|
|
|
|
|
|
|
my @key_fields = (ref $key_field) ? @$key_field : ($key_field); |
1921
|
|
|
|
|
|
|
my @key_indexes; |
1922
|
|
|
|
|
|
|
my $num_of_fields = $sth->FETCH('NUM_OF_FIELDS'); |
1923
|
|
|
|
|
|
|
foreach (@key_fields) { |
1924
|
|
|
|
|
|
|
my $index = $names_hash->{$_}; # perl index not column |
1925
|
|
|
|
|
|
|
$index = $_ - 1 if !defined $index && DBI::looks_like_number($_) && $_>=1 && $_ <= $num_of_fields; |
1926
|
|
|
|
|
|
|
return $sth->set_err($DBI::stderr, "Field '$_' does not exist (not one of @{[keys %$names_hash]})") |
1927
|
|
|
|
|
|
|
unless defined $index; |
1928
|
|
|
|
|
|
|
push @key_indexes, $index; |
1929
|
|
|
|
|
|
|
} |
1930
|
|
|
|
|
|
|
my $rows = {}; |
1931
|
|
|
|
|
|
|
my $NAME = $sth->FETCH($hash_key_name); |
1932
|
|
|
|
|
|
|
my @row = (undef) x $num_of_fields; |
1933
|
|
|
|
|
|
|
$sth->bind_columns(\(@row)); |
1934
|
|
|
|
|
|
|
while ($sth->fetch) { |
1935
|
|
|
|
|
|
|
my $ref = $rows; |
1936
|
|
|
|
|
|
|
$ref = $ref->{$row[$_]} ||= {} for @key_indexes; |
1937
|
|
|
|
|
|
|
@{$ref}{@$NAME} = @row; |
1938
|
|
|
|
|
|
|
} |
1939
|
|
|
|
|
|
|
return $rows; |
1940
|
|
|
|
|
|
|
} |
1941
|
|
|
|
|
|
|
|
1942
|
|
|
|
|
|
|
*dump_results = \&DBI::dump_results; |
1943
|
|
|
|
|
|
|
|
1944
|
|
|
|
|
|
|
sub blob_copy_to_file { # returns length or undef on error |
1945
|
|
|
|
|
|
|
my($self, $field, $filename_or_handleref, $blocksize) = @_; |
1946
|
|
|
|
|
|
|
my $fh = $filename_or_handleref; |
1947
|
|
|
|
|
|
|
my($len, $buf) = (0, ""); |
1948
|
|
|
|
|
|
|
$blocksize ||= 512; # not too ambitious |
1949
|
|
|
|
|
|
|
local(*FH); |
1950
|
|
|
|
|
|
|
unless(ref $fh) { |
1951
|
|
|
|
|
|
|
open(FH, ">$fh") || return undef; |
1952
|
|
|
|
|
|
|
$fh = \*FH; |
1953
|
|
|
|
|
|
|
} |
1954
|
|
|
|
|
|
|
while(defined($self->blob_read($field, $len, $blocksize, \$buf))) { |
1955
|
|
|
|
|
|
|
print $fh $buf; |
1956
|
|
|
|
|
|
|
$len += length $buf; |
1957
|
|
|
|
|
|
|
} |
1958
|
|
|
|
|
|
|
close(FH); |
1959
|
|
|
|
|
|
|
$len; |
1960
|
|
|
|
|
|
|
} |
1961
|
|
|
|
|
|
|
|
1962
|
|
|
|
|
|
|
sub more_results { |
1963
|
|
|
|
|
|
|
shift->{syb_more_results}; # handy grandfathering |
1964
|
|
|
|
|
|
|
} |
1965
|
|
|
|
|
|
|
|
1966
|
|
|
|
|
|
|
} |
1967
|
|
|
|
|
|
|
|
1968
|
|
|
|
|
|
|
unless ($DBI::PurePerl) { # See install_driver |
1969
|
|
|
|
|
|
|
{ @DBD::_mem::dr::ISA = qw(DBD::_mem::common); } |
1970
|
|
|
|
|
|
|
{ @DBD::_mem::db::ISA = qw(DBD::_mem::common); } |
1971
|
|
|
|
|
|
|
{ @DBD::_mem::st::ISA = qw(DBD::_mem::common); } |
1972
|
|
|
|
|
|
|
# DBD::_mem::common::DESTROY is implemented in DBI.xs |
1973
|
|
|
|
|
|
|
} |
1974
|
|
|
|
|
|
|
|
1975
|
|
|
|
|
|
|
1; |
1976
|
|
|
|
|
|
|
__END__ |