📄 dbi.pm
字号:
for $loop (1..$loops) { my @cons; print "Connecting... " if $verb; for (1..$par) { print "$_ "; push @cons, ($drh->connect($dsn,$dbuser,$dbpass) or Carp::croak("connect failed: $DBI::errstr\n")); } print "\nDisconnecting...\n" if $verb; for (@cons) { $_->disconnect or warn "disconnect failed: $DBI::errstr" } } my $t2 = dbi_time(); my $td = $t2 - $t1; printf "$meth %d and disconnect them, %d times: %.4fs / %d = %.4fs\n", $par, $loops, $td, $loops*$par, $td/($loops*$par); return $td;}# Help people doing DBI->errstr, might even document it one day# XXX probably best moved to cheaper XS code if this gets documentedsub err { $DBI::err }sub errstr { $DBI::errstr }# --- Private Internal Function for Creating New DBI Handles# XXX move to PurePerl?*DBI::dr::TIEHASH = \&DBI::st::TIEHASH;*DBI::db::TIEHASH = \&DBI::st::TIEHASH;# These three special constructors are called by the drivers# The way they are called is likely to change.our $shared_profile;sub _new_drh { # called by DBD::<drivername>::driver() my ($class, $initial_attr, $imp_data) = @_; # Provide default storage for State,Err and Errstr. # Note that these are shared by all child handles by default! XXX # State must be undef to get automatic faking in DBI::var::FETCH my ($h_state_store, $h_err_store, $h_errstr_store) = (undef, 0, ''); my $attr = { # these attributes get copied down to child handles by default 'State' => \$h_state_store, # Holder for DBI::state 'Err' => \$h_err_store, # Holder for DBI::err 'Errstr' => \$h_errstr_store, # Holder for DBI::errstr 'TraceLevel' => 0, FetchHashKeyName=> 'NAME', %$initial_attr, }; my ($h, $i) = _new_handle('DBI::dr', '', $attr, $imp_data, $class); # XXX DBI_PROFILE unless DBI::PurePerl because for some reason # it kills the t/zz_*_pp.t tests (they silently exit early) if ($ENV{DBI_PROFILE} && !$DBI::PurePerl) { # The profile object created here when the first driver is loaded # is shared by all drivers so we end up with just one set of profile # data and thus the 'total time in DBI' is really the true total. if (!$shared_profile) { # first time $h->{Profile} = $ENV{DBI_PROFILE}; $shared_profile = $h->{Profile}; } else { $h->{Profile} = $shared_profile; } } return $h unless wantarray; ($h, $i);}sub _new_dbh { # called by DBD::<drivername>::dr::connect() my ($drh, $attr, $imp_data) = @_; my $imp_class = $drh->{ImplementorClass} or Carp::croak("DBI _new_dbh: $drh has no ImplementorClass"); substr($imp_class,-4,4) = '::db'; my $app_class = ref $drh; substr($app_class,-4,4) = '::db'; $attr->{Err} ||= \my $err; $attr->{Errstr} ||= \my $errstr; $attr->{State} ||= \my $state; _new_handle($app_class, $drh, $attr, $imp_data, $imp_class);}sub _new_sth { # called by DBD::<drivername>::db::prepare) my ($dbh, $attr, $imp_data) = @_; my $imp_class = $dbh->{ImplementorClass} or Carp::croak("DBI _new_sth: $dbh has no ImplementorClass"); substr($imp_class,-4,4) = '::st'; my $app_class = ref $dbh; substr($app_class,-4,4) = '::st'; _new_handle($app_class, $dbh, $attr, $imp_data, $imp_class);}# end of DBI package# --------------------------------------------------------------------# === The internal DBI Switch pseudo 'driver' class ==={ package # hide from PAUSE DBD::Switch::dr; DBI->setup_driver('DBD::Switch'); # sets up @ISA $DBD::Switch::dr::imp_data_size = 0; $DBD::Switch::dr::imp_data_size = 0; # avoid typo warning my $drh; sub driver { return $drh if $drh; # a package global my $inner; ($drh, $inner) = DBI::_new_drh('DBD::Switch::dr', { 'Name' => 'Switch', 'Version' => $DBI::VERSION, 'Attribution' => "DBI $DBI::VERSION by Tim Bunce", }); Carp::croak("DBD::Switch init failed!") unless ($drh && $inner); return $drh; } sub CLONE { undef $drh; } sub FETCH { my($drh, $key) = @_; return DBI->trace if $key eq 'DebugDispatch'; return undef if $key eq 'DebugLog'; # not worth fetching, sorry return $drh->DBD::_::dr::FETCH($key); undef; } sub STORE { my($drh, $key, $value) = @_; if ($key eq 'DebugDispatch') { DBI->trace($value); } elsif ($key eq 'DebugLog') { DBI->trace(-1, $value); } else { $drh->DBD::_::dr::STORE($key, $value); } }}# --------------------------------------------------------------------# === OPTIONAL MINIMAL BASE CLASSES FOR DBI SUBCLASSES ===# We only define default methods for harmless functions.# We don't, for example, define a DBD::_::st::prepare(){ package # hide from PAUSE DBD::_::common; # ====== Common base class methods ====== use strict; # methods common to all handle types: sub _not_impl { my ($h, $method) = @_; $h->trace_msg("Driver does not implement the $method method.\n"); return; # empty list / undef } # generic TIEHASH default methods: sub FIRSTKEY { } sub NEXTKEY { } sub EXISTS { defined($_[0]->FETCH($_[1])) } # XXX undef? sub CLEAR { Carp::carp "Can't CLEAR $_[0] (DBI)" } sub FETCH_many { # XXX should move to C one day my $h = shift; return map { $h->FETCH($_) } @_; } *dump_handle = \&DBI::dump_handle; sub install_method { # special class method called directly by apps and/or drivers # to install new methods into the DBI dispatcher # DBD::Foo::db->install_method("foo_mumble", { usage => [...], options => '...' }); my ($class, $method, $attr) = @_; Carp::croak("Class '$class' must begin with DBD:: and end with ::db or ::st") unless $class =~ /^DBD::(\w+)::(dr|db|st)$/; my ($driver, $subtype) = ($1, $2); Carp::croak("invalid method name '$method'") unless $method =~ m/^([a-z]+_)\w+$/; my $prefix = $1; my $reg_info = $dbd_prefix_registry->{$prefix}; Carp::carp("method name prefix '$prefix' is not associated with a registered driver") unless $reg_info; my $full_method = "DBI::${subtype}::$method"; $DBI::installed_methods{$full_method} = $attr; my (undef, $filename, $line) = caller; # XXX reformat $attr as needed for _install_method my %attr = %{$attr||{}}; # copy so we can edit DBI->_install_method("DBI::${subtype}::$method", "$filename at line $line", \%attr); } sub parse_trace_flags { my ($h, $spec) = @_; my $level = 0; my $flags = 0; my @unknown; for my $word (split /\s*[|&,]\s*/, $spec) { if (DBI::looks_like_number($word) && $word <= 0xF && $word >= 0) { $level = $word; } elsif ($word eq 'ALL') { $flags = 0x7FFFFFFF; # XXX last bit causes negative headaches last; } elsif (my $flag = $h->parse_trace_flag($word)) { $flags |= $flag; } else { push @unknown, $word; } } if (@unknown && (ref $h ? $h->FETCH('Warn') : 1)) { Carp::carp("$h->parse_trace_flags($spec) ignored unknown trace flags: ". join(" ", map { DBI::neat($_) } @unknown)); } $flags |= $level; return $flags; } sub parse_trace_flag { my ($h, $name) = @_; # 0xddDDDDrL (driver, DBI, reserved, Level) return 0x00000100 if $name eq 'SQL'; return; } sub private_attribute_info { return undef; }}{ package # hide from PAUSE DBD::_::dr; # ====== DRIVER ====== @DBD::_::dr::ISA = qw(DBD::_::common); use strict; sub default_user { my ($drh, $user, $pass, $attr) = @_; $user = $ENV{DBI_USER} unless defined $user; $pass = $ENV{DBI_PASS} unless defined $pass; return ($user, $pass); } sub connect { # normally overridden, but a handy default my ($drh, $dsn, $user, $auth) = @_; my ($this) = DBI::_new_dbh($drh, { 'Name' => $dsn, }); # XXX debatable as there's no "server side" here # (and now many uses would trigger warnings on DESTROY) # $this->STORE(Active => 1); # so drivers should set it in their own connect $this; } sub connect_cached { my $drh = shift; my ($dsn, $user, $auth, $attr) = @_; my $cache = $drh->{CachedKids} ||= {}; my @attr_keys = $attr ? sort keys %$attr : (); my $key = do { local $^W; # silence undef warnings join "~~", $dsn, $user, $auth, $attr ? (@attr_keys,@{$attr}{@attr_keys}) : () }; my $dbh = $cache->{$key}; $drh->trace_msg(sprintf(" connect_cached: key '$key', cached dbh $dbh\n", DBI::neat($key), DBI::neat($dbh))) if $DBI::dbi_debug >= 4; my $cb = $attr->{Callbacks}; # take care not to autovivify if ($dbh && $dbh->FETCH('Active') && eval { $dbh->ping }) { # If the caller has provided a callback then call it if ($cb and $cb = $cb->{"connect_cached.reused"}) { local $_ = "connect_cached.reused"; $cb->($dbh, $dsn, $user, $auth, $attr); } return $dbh; } # If the caller has provided a callback then call it if ($cb and $cb = $cb->{"connect_cached.new"}) { local $_ = "connect_cached.new"; $cb->($dbh, $dsn, $user, $auth, $attr); } $dbh = $drh->connect(@_); $cache->{$key} = $dbh; # replace prev entry, even if connect failed return $dbh; }}{ package # hide from PAUSE DBD::_::db; # ====== DATABASE ====== @DBD::_::db::ISA = qw(DBD::_::common); use strict; sub clone { my ($old_dbh, $attr) = @_; my $closure = $old_dbh->{dbi_connect_closure} or return; unless ($attr) { # copy attributes visible in the attribute cache keys %$old_dbh; # reset iterator while ( my ($k, $v) = each %$old_dbh ) { # ignore non-code refs, i.e., caches, handles, Err etc next if ref $v && ref $v ne 'CODE'; # HandleError etc $attr->{$k} = $v; } # explicitly set attributes which are unlikely to be in the # attribute cache, i.e., boolean's and some others $attr->{$_} = $old_dbh->FETCH($_) for (qw( AutoCommit ChopBlanks InactiveDestroy LongTruncOk PrintError PrintWarn Profile RaiseError ShowErrorStatement TaintIn TaintOut )); } # use Data::Dumper; warn Dumper([$old_dbh, $attr]); my $new_dbh = &$closure($old_dbh, $attr); unless ($new_dbh) { # need to copy err/errstr from driver back into $old_dbh my $drh = $old_dbh->{Driver}; return $old_dbh->set_err($drh->err, $drh->errstr, $drh->state); } return $new_dbh; } sub quote_identifier { my ($dbh, @id) = @_; my $attr = (@id > 3 && ref($id[-1])) ? pop @id : undef; my $info = $dbh->{dbi_quote_identifier_cache} ||= [ $dbh->get_info(29) || '"', # SQL_IDENTIFIER_QUOTE_CHAR $dbh->get_info(41) || '.', # SQL_CATALOG_NAME_SEPARATOR $dbh->get_info(114) || 1, # SQL_CATALOG_LOCATION ]; my $quote = $info->[0]; foreach (@id) { # quote the elements next unless defined; s/$quote/$quote$quote/g; # escape embedded quotes $_ = qq{$quote$_$quote}; } # strip out catalog if present for special handling my $catalog = (@id >= 3) ? shift @id : undef; # join the dots, ignoring any null/undef elements (ie schema) my $quoted_id = join '.', grep { defined } @id; if ($catalog) { # add catalog correctly $quoted_id = ($info->[2] == 2) # SQL_CL_END ? $quoted_id . $info->[1] . $catalog : $catalog . $info->[1] . $quoted_id; } return $quoted_id; } sub quote { my ($dbh, $str, $data_type) = @_; return "NULL" unless defined $str; unless ($data_type) { $str =~ s/'/''/g; # ISO SQL2 return "'$str'"; } my $dbi_literal_quote_cache = $dbh->{'dbi_literal_quote_cache'} ||= [ {} , {} ]; my ($prefixes, $suffixes) = @$dbi_literal_quote_cache; my $lp = $prefixes->{$data_type}; my $ls = $suffixes->{$data_type}; if ( ! defined $lp || ! defined $ls ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -