📄 dbd.pm
字号:
$method_name must being with 'C<ora_>', and for DBD::AnyData itmust begin with 'C<ad_>'.The attributes can be used to provide fine control over how the DBIdispatcher handles the dispatching of the method. However, at thispoint, it's undocumented and very liable to change. (Volunteers topolish up and document the interface are very welcome to get intouch via dbi-dev@perl.org)Methods installed using install_method default to the standard errorhandling behaviour for DBI methods: clearing err and errstr beforecalling the method, and checking for errors to trigger RaiseError etc. on return. This differs from the default behaviour of func(). Note for driver authors: The DBD::Foo::xx->install_method call won'twork until the class-hierarchy has been setup. Normally the DBIlooks after that just after the driver is loaded. This meansinstall_method() can't be called at the time the driver is loadedunless the class-hierarchy is set up first. The way to do that isto call the setup_driver() method: DBI->setup_driver('DBD::Foo');before using install_method().=head4 The CLONE special subroutineAlso needed here, in the B<DBD::Driver> package, is a C<CLONE()> methodthat will be called by perl when an intrepreter is cloned. All yourC<CLONE()> method needs to do, currently, is clear the cached I<$drh> sothe new interpreter won't start using the cached I<$drh> from the oldinterpreter: sub CLONE { undef $drh; }See L<http://search.cpan.org/dist/perl/pod/perlmod.pod#Making_your_module_threadsafe>for details.=head3 The DBD::Driver::dr packageThe next lines of code look as follows: package DBD::Driver::dr; # ====== DRIVER ====== $DBD::Driver::dr::imp_data_size = 0;Note that no I<@ISA> is needed here, or for the other B<DBD::Driver::*>classes, because the B<DBI> takes care of that for you when the driver isloaded. *FIX ME* Explain what the imp_data_size is, so that implementors aren't practicing cargo-cult programming.=head4 The database handle constructorThe database handle constructor is the driver's (hence the changednamespace) C<connect()> method: sub connect { my ($drh, $dr_dsn, $user, $auth, $attr) = @_; # Some database specific verifications, default settings # and the like can go here. This should only include # syntax checks or similar stuff where it's legal to # 'die' in case of errors. # For example, many database packages requires specific # environment variables to be set; this could be where you # validate that they are set, or default them if they are not set. my $driver_prefix = "drv_"; # the assigned prefix for this driver # Process attributes from the DSN; we assume ODBC syntax # here, that is, the DSN looks like var1=val1;...;varN=valN foreach my $var ( split /;/, $dr_dsn ) { my ($attr_name, $attr_value) = split '=', $var, 2; return $drh->set_err($DBI::stderr, "Can't parse DSN part '$var'") unless defined $attr_value; # add driver prefix to attribute name if it doesn't have it already $attr_name = $driver_prefix.$attr_name unless $attr_name =~ /^$driver_prefix/o; # Store attribute into %$attr, replacing any existing value. # The DBI will STORE() these into $dbh after we've connected $attr->{$attr_name} = $attr_value; } # Get the attributes we'll use to connect. # We use delete here because these no need to STORE them my $db = delete $attr->{drv_database} || delete $attr->{drv_db} or return $drh->set_err($DBI::stderr, "No database name given in DSN '$dr_dsn'"); my $host = delete $attr->{drv_host} || 'localhost'; my $port = delete $attr->{drv_port} || 123456; # Assume you can attach to your database via drv_connect: my $connection = drv_connect($db, $host, $port, $user, $auth) or return $drh->set_err($DBI::stderr, "Can't connect to $dr_dsn: ..."); # create a 'blank' dbh (call superclass constructor) my ($outer, $dbh) = DBI::_new_dbh($drh, { Name => $dr_dsn }); $dbh->STORE('Active', 1 ); $dbh->{drv_connection} = $connection; return $outer; }This is mostly the same as in the I<driver handle constructor> above.The arguments are described in L<DBI>.The constructor C<DBI::_new_dbh()> is called, returning a database handle.The constructor's prototype is: ($outer, $inner) = DBI::_new_dbh($drh, $public_attr, $private_attr);with similar arguments to those in the I<driver handle constructor>,except that the I<$class> is replaced by I<$drh>. The I<Name> attributeis a standard B<DBI> attribute (see L<DBI/Database Handle Attributes>).In scalar context, only the outer handle is returned.Note the use of the C<STORE()> method for setting the I<dbh> attributes.That's because within the driver code, the handle object you have isthe 'inner' handle of a tied hash, not the outer handle that theusers of your driver have.Because you have the inner handle, tie magic doesn't get invokedwhen you get or set values in the hash. This is often very handy forspeed when you want to get or set simple non-special driver-specificattributes.However, some attribute values, such as those handled by the B<DBI> likeI<PrintError>, don't actually exist in the hash and must be read viaC<$h-E<gt>FETCH($attrib)> and set via C<$h-E<gt>STORE($attrib, $value)>.If in any doubt, use these methods.=head4 The data_sources() methodThe C<data_sources()> method must populate and return a list of valid datasources, prefixed with the "I<dbi:Driver>" incantation that allows them tobe used in the first argument of the C<DBI-E<gt>connect()> method.An example of this might be scanning the F<$HOME/.odbcini> file on Unixfor ODBC data sources (DSNs).As a trivial example, consider a fixed list of data sources: sub data_sources { my($drh, $attr) = @_; my(@list) = (); # You need more sophisticated code than this to set @list... push @list, "dbi:Driver:abc"; push @list, "dbi:Driver:def"; push @list, "dbi:Driver:ghi"; # End of code to set @list return @list; }=head4 The disconnect_all() methodIf you need to release any resources when the driver is unloaded, youcan provide a disconnect_all method.=head4 Other driver handle methodsIf you need any other driver handle methods, they can follow here.=head4 Error handlingIt is quite likely that something fails in the connect method.With B<DBD::File> for example, you might catch an error when setting thecurrent directory to something not existent by using the(driver-specific) I<f_dir> attribute.To report an error, you use the C<set_err()> method: $h->set_err($err, $errmsg, $state);This will ensure that the error is recorded correctly and thatI<RaiseError> and I<PrintError> etc are handled correctly.Typically you'll always use the method instance, aka your method's firstargument.As C<set_err()> always returns C<undef> your error handling code canusually be simplified to something like this: return $h->set_err($err, $errmsg, $state) if ...;=head3 The DBD::Driver::db package package DBD::Driver::db; # ====== DATABASE ====== $DBD::Driver::db::imp_data_size = 0;=head4 The statement handle constructorThere's nothing much new in the statement handle constructor, whichis the C<prepare()> method: sub prepare { my ($dbh, $statement, @attribs) = @_; # create a 'blank' sth my ($outer, $sth) = DBI::_new_sth($dbh, { Statement => $statement }); $sth->STORE('NUM_OF_PARAMS', ($statement =~ tr/?//)); $sth->{drv_params} = []; return $outer; }This is still the same -- check the arguments and call the super classconstructor C<DBI::_new_sth()>. Again, in scalar context, only the outerhandle is returned. The I<Statement> attribute should be cached asshown.Note the prefix I<drv_> in the attribute names: it is required thatall your private attributes use a lowercase prefix unique to your driver.As mentioned earlier in this document, the B<DBI> contains a registry ofknown driver prefixes and may one day warn about unknown attributesthat don't have a registered prefix.Note that we parse the statement here in order to set the attributeI<NUM_OF_PARAMS>. The technique illustrated is not very reliable; it canbe confused by question marks appearing in quoted strings, delimitedidentifiers or in SQL comments that are part of the SQL statement. Wecould set I<NUM_OF_PARAMS> in the C<execute()> method instead becausethe B<DBI> specification explicitly allows a driver to defer this, but thenthe user could not call C<bind_param()>.=head4 Transaction handlingPure Perl drivers will rarely support transactions. Thus your C<commit()>and C<rollback()> methods will typically be quite simple: sub commit { my ($dbh) = @_; if ($dbh->FETCH('Warn')) { warn("Commit ineffective while AutoCommit is on"); } 0; } sub rollback { my ($dbh) = @_; if ($dbh->FETCH('Warn')) { warn("Rollback ineffective while AutoCommit is on"); } 0; }Or even simpler, just use the default methods provided by the B<DBI> thatdo nothing except return C<undef>.The B<DBI>'s default C<begin_work()> method can be used by inheritance.=head4 The STORE() and FETCH() methodsThese methods (that we have already used, see above) are called foryou, whenever the user does a: $dbh->{$attr} = $val;or, respectively, $val = $dbh->{$attr};See L<perltie> for details on tied hash refs to understand why thesemethods are required.The B<DBI> will handle most attributes for you, in particular attributeslike I<RaiseError> or I<PrintError>. All you have to do is handle yourdriver's private attributes and any attributes, like I<AutoCommit> andI<ChopBlanks>, that the B<DBI> can't handle for you.A good example might look like this: sub STORE { my ($dbh, $attr, $val) = @_; if ($attr eq 'AutoCommit') { # AutoCommit is currently the only standard attribute we have # to consider. if (!$val) { die "Can't disable AutoCommit"; } return 1; } if ($attr =~ m/^drv_/) { # Handle only our private attributes here # Note that we could trigger arbitrary actions. # Ideally we should warn about unknown attributes. $dbh->{$attr} = $val; # Yes, we are allowed to do this, return 1; # but only for our private attributes } # Else pass up to DBI to handle for us $dbh->SUPER::STORE($attr, $val); } sub FETCH { my ($dbh, $attr) = @_; if ($attr eq 'AutoCommit') { return 1; } if ($attr =~ m/^drv_/) { # Handle only our private attributes here # Note that we could trigger arbitrary actions. return $dbh->{$attr}; # Yes, we are allowed to do this, # but only for our private attributes } # Else pass up to DBI to handle $dbh->SUPER::FETCH($attr); }The B<DBI> will actually store and fetch driver-specific attributes (with alllowercase names) without warning or error, so there's actually no need toimplement driver-specific any code in your C<FETCH()> and C<STORE()>methods unless you need extra logic/checks, beyond getting or settingthe value.Unless your driver documentation indicates otherwise, the return value ofthe C<STORE()> method is unspecified and the caller shouldn't use that value.=head4 Other database handle methodsAs with the driver package, other database handle methods may follow here.In particular you should consider a (possibly empty) C<disconnect()>method and possibly a C<quote()> method if B<DBI>'s default isn't correct foryou. You may also need the C<type_info_all()> and C<get_info()> methods,as described elsewhere in this document.Where reasonable use C<$h-E<gt>SUPER::foo()> to call the B<DBI>'s method insome or all cases and just wrap your custom behavior around that.If you want to use private trace flags you'll probably want to beable to set them by name. To do that you'll need to define aC<parse_trace_flag()> method (note that's "parse_trace_flag", singular,not "parse_trace_flags", plural). sub parse_trace_flag { my ($h, $name) = @_; return 0x01000000 if $name eq 'foo'; return 0x02000000 if $name eq 'bar'; return 0x04000000 if $name eq 'baz'; return 0x08000000 if $name eq 'boo'; return 0x10000000 if $name eq 'bop'; return $h->SUPER::parse_trace_flag($name); }All private flag names must be lowercase, and all private flagsmust be in the top 8 of the 32 bits.=head3 The DBD::Driver::st packageThis package follows the same pattern the others do: package DBD::Driver::st; $DBD::Driver::st::imp_data_size = 0;=head4 The execute() and bind_param() methodsThis is perhaps the most difficult method because we have to considerparameter bindings here. In addition to that, there are a number ofstatement attributes which must be set for inherited B<DBI> methods to
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -