📄 dbi.pm
字号:
} Carp::croak("install_driver($driver) failed: $err$advice\n"); } if ($DBI::dbi_debug) { no strict 'refs'; (my $driver_file = $driver_class) =~ s/::/\//g; my $dbd_ver = ${"$driver_class\::VERSION"} || "undef"; $class->trace_msg(" install_driver: $driver_class version $dbd_ver" ." loaded from $INC{qq($driver_file.pm)}\n"); } # --- do some behind-the-scenes checks and setups on the driver $class->setup_driver($driver_class); # --- run the driver function $drh = eval { $driver_class->driver($attr || {}) }; unless ($drh && ref $drh && !$@) { my $advice = ""; $@ ||= "$driver_class->driver didn't return a handle"; # catch people on case in-sensitive systems using the wrong case $advice = "\nPerhaps the capitalisation of DBD '$driver' isn't right." if $@ =~ /locate object method/; Carp::croak("$driver_class initialisation failed: $@$advice"); } $DBI::installed_drh{$driver} = $drh; $class->trace_msg(" <- install_driver= $drh\n") if $DBI::dbi_debug; $drh;}*driver = \&install_driver; # currently an alias, may changesub setup_driver { my ($class, $driver_class) = @_; my $type; foreach $type (qw(dr db st)){ my $class = $driver_class."::$type"; no strict 'refs'; push @{"${class}::ISA"}, "DBD::_::$type" unless UNIVERSAL::isa($class, "DBD::_::$type"); my $mem_class = "DBD::_mem::$type"; push @{"${class}_mem::ISA"}, $mem_class unless UNIVERSAL::isa("${class}_mem", $mem_class) or $DBI::PurePerl; }}sub _rebless { my $dbh = shift; my ($outer, $inner) = DBI::_handles($dbh); my $class = shift(@_).'::db'; bless $inner => $class; bless $outer => $class; # outer last for return}sub _set_isa { my ($classes, $topclass) = @_; my $trace = DBI->trace_msg(" _set_isa([@$classes])\n"); foreach my $suffix ('::db','::st') { my $previous = $topclass || 'DBI'; # trees are rooted here foreach my $class (@$classes) { my $base_class = $previous.$suffix; my $sub_class = $class.$suffix; my $sub_class_isa = "${sub_class}::ISA"; no strict 'refs'; if (@$sub_class_isa) { DBI->trace_msg(" $sub_class_isa skipped (already set to @$sub_class_isa)\n") if $trace; } else { @$sub_class_isa = ($base_class) unless @$sub_class_isa; DBI->trace_msg(" $sub_class_isa = $base_class\n") if $trace; } $previous = $class; } }}sub _rebless_dbtype_subclass { my ($dbh, $rootclass, $DbTypeSubclass) = @_; # determine the db type names for class hierarchy my @hierarchy = DBI::_dbtype_names($dbh, $DbTypeSubclass); # add the rootclass prefix to each ('DBI::' or 'MyDBI::' etc) $_ = $rootclass.'::'.$_ foreach (@hierarchy); # load the modules from the 'top down' DBI::_load_class($_, 1) foreach (reverse @hierarchy); # setup class hierarchy if needed, does both '::db' and '::st' DBI::_set_isa(\@hierarchy, $rootclass); # finally bless the handle into the subclass DBI::_rebless($dbh, $hierarchy[0]);}sub _dbtype_names { # list dbtypes for hierarchy, ie Informix=>ADO=>ODBC my ($dbh, $DbTypeSubclass) = @_; if ($DbTypeSubclass && $DbTypeSubclass ne '1' && ref $DbTypeSubclass ne 'CODE') { # treat $DbTypeSubclass as a comma separated list of names my @dbtypes = split /\s*,\s*/, $DbTypeSubclass; $dbh->trace_msg(" DbTypeSubclass($DbTypeSubclass)=@dbtypes (explicit)\n"); return @dbtypes; } # XXX will call $dbh->get_info(17) (=SQL_DBMS_NAME) in future? my $driver = $dbh->{Driver}->{Name}; if ( $driver eq 'Proxy' ) { # XXX Looking into the internals of DBD::Proxy is questionable! ($driver) = $dbh->{proxy_client}->{application} =~ /^DBI:(.+?):/i or die "Can't determine driver name from proxy"; } my @dbtypes = (ucfirst($driver)); if ($driver eq 'ODBC' || $driver eq 'ADO') { # XXX will move these out and make extensible later: my $_dbtype_name_regexp = 'Oracle'; # eg 'Oracle|Foo|Bar' my %_dbtype_name_map = ( 'Microsoft SQL Server' => 'MSSQL', 'SQL Server' => 'Sybase', 'Adaptive Server Anywhere' => 'ASAny', 'ADABAS D' => 'AdabasD', ); my $name; $name = $dbh->func(17, 'GetInfo') # SQL_DBMS_NAME if $driver eq 'ODBC'; $name = $dbh->{ado_conn}->Properties->Item('DBMS Name')->Value if $driver eq 'ADO'; die "Can't determine driver name! ($DBI::errstr)\n" unless $name; my $dbtype; if ($_dbtype_name_map{$name}) { $dbtype = $_dbtype_name_map{$name}; } else { if ($name =~ /($_dbtype_name_regexp)/) { $dbtype = lc($1); } else { # generic mangling for other names: $dbtype = lc($name); } $dbtype =~ s/\b(\w)/\U$1/g; $dbtype =~ s/\W+/_/g; } # add ODBC 'behind' ADO push @dbtypes, 'ODBC' if $driver eq 'ADO'; # add discovered dbtype in front of ADO/ODBC unshift @dbtypes, $dbtype; } @dbtypes = &$DbTypeSubclass($dbh, \@dbtypes) if (ref $DbTypeSubclass eq 'CODE'); $dbh->trace_msg(" DbTypeSubclass($DbTypeSubclass)=@dbtypes\n"); return @dbtypes;}sub _load_class { my ($load_class, $missing_ok) = @_; DBI->trace_msg(" _load_class($load_class, $missing_ok)\n", 2); no strict 'refs'; return 1 if @{"$load_class\::ISA"}; # already loaded/exists (my $module = $load_class) =~ s!::!/!g; DBI->trace_msg(" _load_class require $module\n", 2); eval { require "$module.pm"; }; return 1 unless $@; return 0 if $missing_ok && $@ =~ /^Can't locate \Q$module.pm\E/; die $@;}sub init_rootclass { # deprecated return 1;}*internal = \&DBD::Switch::dr::driver;sub available_drivers { my($quiet) = @_; my(@drivers, $d, $f); local(*DBI::DIR, $@); my(%seen_dir, %seen_dbd); my $haveFileSpec = eval { require File::Spec }; foreach $d (@INC){ chomp($d); # Perl 5 beta 3 bug in #!./perl -Ilib from Test::Harness my $dbd_dir = ($haveFileSpec ? File::Spec->catdir($d, 'DBD') : "$d/DBD"); next unless -d $dbd_dir; next if $seen_dir{$d}; $seen_dir{$d} = 1; # XXX we have a problem here with case insensitive file systems # XXX since we can't tell what case must be used when loading. opendir(DBI::DIR, $dbd_dir) || Carp::carp "opendir $dbd_dir: $!\n"; foreach $f (readdir(DBI::DIR)){ next unless $f =~ s/\.pm$//; next if $f eq 'NullP'; if ($seen_dbd{$f}){ Carp::carp "DBD::$f in $d is hidden by DBD::$f in $seen_dbd{$f}\n" unless $quiet; } else { push(@drivers, $f); } $seen_dbd{$f} = $d; } closedir(DBI::DIR); } # "return sort @drivers" will not DWIM in scalar context. return wantarray ? sort @drivers : @drivers;}sub installed_versions { my ($class, $quiet) = @_; my %error; my %version = ( DBI => $DBI::VERSION ); $version{"DBI::PurePerl"} = $DBI::PurePerl::VERSION if $DBI::PurePerl; for my $driver ($class->available_drivers($quiet)) { next if $DBI::PurePerl && grep { -d "$_/auto/DBD/$driver" } @INC; my $drh = eval { local $SIG{__WARN__} = sub {}; $class->install_driver($driver); }; ($error{"DBD::$driver"}=$@),next if $@; no strict 'refs'; my $vers = ${"DBD::$driver" . '::VERSION'}; $version{"DBD::$driver"} = $vers || '?'; } if (wantarray) { return map { m/^DBD::(\w+)/ ? ($1) : () } sort keys %version; } if (!defined wantarray) { # void context require Config; # add more detail $version{OS} = "$^O\t($Config::Config{osvers})"; $version{Perl} = "$]\t($Config::Config{archname})"; $version{$_} = (($error{$_} =~ s/ \(\@INC.*//s),$error{$_}) for keys %error; printf " %-16s: %s\n",$_,$version{$_} for reverse sort keys %version; } return \%version;}sub data_sources { my ($class, $driver, @other) = @_; my $drh = $class->install_driver($driver); my @ds = $drh->data_sources(@other); return @ds;}sub neat_list { my ($listref, $maxlen, $sep) = @_; $maxlen = 0 unless defined $maxlen; # 0 == use internal default $sep = ", " unless defined $sep; join($sep, map { neat($_,$maxlen) } @$listref);}sub dump_results { # also aliased as a method in DBD::_::st my ($sth, $maxlen, $lsep, $fsep, $fh) = @_; return 0 unless $sth; $maxlen ||= 35; $lsep ||= "\n"; $fh ||= \*STDOUT; my $rows = 0; my $ref; while($ref = $sth->fetch) { print $fh $lsep if $rows++ and $lsep; my $str = neat_list($ref,$maxlen,$fsep); print $fh $str; # done on two lines to avoid 5.003 errors } print $fh "\n$rows rows".($DBI::err ? " ($DBI::err: $DBI::errstr)" : "")."\n"; $rows;}sub data_diff { my ($a, $b, $logical) = @_; my $diff = data_string_diff($a, $b); return "" if $logical and !$diff; my $a_desc = data_string_desc($a); my $b_desc = data_string_desc($b); return "" if !$diff and $a_desc eq $b_desc; $diff ||= "Strings contain the same sequence of characters" if length($a); $diff .= "\n" if $diff; return "a: $a_desc\nb: $b_desc\n$diff";}sub data_string_diff { # Compares 'logical' characters, not bytes, so a latin1 string and an # an equivalent unicode string will compare as equal even though their # byte encodings are different. my ($a, $b) = @_; unless (defined $a and defined $b) { # one undef return "" if !defined $a and !defined $b; return "String a is undef, string b has ".length($b)." characters" if !defined $a; return "String b is undef, string a has ".length($a)." characters" if !defined $b; } require utf8; # hack to cater for perl 5.6 *utf8::is_utf8 = sub { (DBI::neat(shift)=~/^"/) } unless defined &utf8::is_utf8; my @a_chars = (utf8::is_utf8($a)) ? unpack("U*", $a) : unpack("C*", $a); my @b_chars = (utf8::is_utf8($b)) ? unpack("U*", $b) : unpack("C*", $b); my $i = 0; while (@a_chars && @b_chars) { ++$i, shift(@a_chars), shift(@b_chars), next if $a_chars[0] == $b_chars[0];# compare ordinal values my @desc = map { $_ > 255 ? # if wide character... sprintf("\\x{%04X}", $_) : # \x{...} chr($_) =~ /[[:cntrl:]]/ ? # else if control character ... sprintf("\\x%02X", $_) : # \x.. chr($_) # else as themselves } ($a_chars[0], $b_chars[0]); # highlight probable double-encoding? foreach my $c ( @desc ) { next unless $c =~ m/\\x\{08(..)}/; $c .= "='" .chr(hex($1)) ."'" } return sprintf "Strings differ at index $i: a[$i]=$desc[0], b[$i]=$desc[1]"; } return "String a truncated after $i characters" if @b_chars; return "String b truncated after $i characters" if @a_chars; return "";}sub data_string_desc { # describe a data string my ($a) = @_; require bytes; require utf8; # hacks to cater for perl 5.6 *utf8::is_utf8 = sub { (DBI::neat(shift)=~/^"/) } unless defined &utf8::is_utf8; *utf8::valid = sub { 1 } unless defined &utf8::valid; # Give sufficient info to help diagnose at least these kinds of situations: # - valid UTF8 byte sequence but UTF8 flag not set # (might be ascii so also need to check for hibit to make it worthwhile) # - UTF8 flag set but invalid UTF8 byte sequence # could do better here, but this'll do for now my $utf8 = sprintf "UTF8 %s%s", utf8::is_utf8($a) ? "on" : "off", utf8::valid($a||'') ? "" : " but INVALID encoding"; return "$utf8, undef" unless defined $a; my $is_ascii = $a =~ m/^[\000-\177]*$/; return sprintf "%s, %s, %d characters %d bytes", $utf8, $is_ascii ? "ASCII" : "non-ASCII", length($a), bytes::length($a);}sub connect_test_perf { my($class, $dsn,$dbuser,$dbpass, $attr) = @_; Carp::croak("connect_test_perf needs hash ref as fourth arg") unless ref $attr; # these are non standard attributes just for this special method my $loops ||= $attr->{dbi_loops} || 5; my $par ||= $attr->{dbi_par} || 1; # parallelism my $verb ||= $attr->{dbi_verb} || 1; my $meth ||= $attr->{dbi_meth} || 'connect'; print "$dsn: testing $loops sets of $par connections:\n"; require "FileHandle.pm"; # don't let toke.c create empty FileHandle package local $| = 1; my $drh = $class->install_driver($dsn) or Carp::croak("Can't install $dsn driver\n"); # test the connection and warm up caches etc $drh->connect($dsn,$dbuser,$dbpass) or Carp::croak("connect failed: $DBI::errstr"); my $t1 = dbi_time(); my $loop;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -