📄 dbi::dbd.3
字号:
with methods such as \f(CW\*(C`prepare()\*(C'\fR;.IP "DBD::Driver::st" 4.IX Item "DBD::Driver::st"with methods such as \f(CW\*(C`execute()\*(C'\fR and \f(CW\*(C`fetch()\*(C'\fR..PPThe \fIDriver.pm\fR file will also contain the documentation specific to\&\fBDBD::Driver\fR in the format used by perldoc..PPIn a pure Perl driver, the \fIDriver.pm\fR file is the core of theimplementation. You will need to provide all the key methods needed by \fB\s-1DBI\s0\fR..PPNow let's take a closer look at an excerpt of \fIFile.pm\fR as an example.We ignore things that are common to any module (even non-DBI modules)or really specific to the \fBDBD::File\fR package..PP\fIThe DBD::Driver package\fR.IX Subsection "The DBD::Driver package".PPThe header.IX Subsection "The header".PP.Vb 1\& package DBD::File;\&\& use strict;\& use vars qw($VERSION $drh);\&\& $VERSION = "1.23.00" # Version number of DBD::File.Ve.PPThis is where the version number of your driver is specified, and iswhere \fIMakefile.PL\fR looks for this information. Please ensure that anyother modules added with your driver are also version stamped so that\&\s-1CPAN\s0 does not get confused..PPIt is recommended that you use a two-part (1.23) or three-part (1.23.45)version number. Also consider the \s-1CPAN\s0 system, which gets confused andconsiders version 1.10 to precede version 1.9, so that using a raw \s-1CVS\s0,\&\s-1RCS\s0 or \s-1SCCS\s0 version number is probably not appropriate (despite beingvery common)..PPFor Subversion you could use:.PP.Vb 1\& $VERSION = sprintf("12.%06d", q$Revision: 12345 $ =~ /(\ed+)/o);.Ve.PP(use lots of leading zeros on the second portion so if you move the code to ashared repository like svn.perl.org the much larger revision numbers won'tcause a problem, at least not for a few years). For \s-1RCS\s0 or \s-1CVS\s0 you can use:.PP.Vb 1\& $VERSION = sprintf "%d.%02d", \*(Aq$Revision: 11.21 $ \*(Aq =~ /(\ed+)\e.(\ed+)/;.Ve.PPwhich pads out the fractional part with leading zeros so all is well(so long as you don't go past x.99).PP.Vb 1\& $drh = undef; # holds driver handle once initialized.Ve.PPThis is where the driver handle will be stored, once created.Note that you may assume there is only one handle for your driver..PPThe driver constructor.IX Subsection "The driver constructor".PPThe \f(CW\*(C`driver()\*(C'\fR method is the driver handle constructor. Note thatthe \f(CW\*(C`driver()\*(C'\fR method is in the \fBDBD::Driver\fR package, not inone of the sub-packages \fBDBD::Driver::dr\fR, \fBDBD::Driver::db\fR, or\&\fBDBD::Driver::db\fR..PP.Vb 4\& sub driver\& {\& return $drh if $drh; # already created \- return same one\& my ($class, $attr) = @_;\&\& $class .= "::dr";\&\& DBD::Driver::db\->install_method(\*(Aqdrv_example_dbh_method\*(Aq);\& DBD::Driver::st\->install_method(\*(Aqdrv_example_sth_method\*(Aq);\&\& # not a \*(Aqmy\*(Aq since we use it above to prevent multiple drivers\& $drh = DBI::_new_drh($class, {\& \*(AqName\*(Aq => \*(AqFile\*(Aq,\& \*(AqVersion\*(Aq => $VERSION,\& \*(AqAttribution\*(Aq => \*(AqDBD::File by Jochen Wiedmann\*(Aq,\& })\& or return undef;\&\& return $drh;\& }.Ve.PPThis is a reasonable example of how \fB\s-1DBI\s0\fR implements its handles. Thereare three kinds: \fBdriver handles\fR (typically stored in \fI\f(CI$drh\fI\fR; fromnow on called \fIdrh\fR or \fI\f(CI$drh\fI\fR), \fBdatabase handles\fR (from now oncalled \fIdbh\fR or \fI\f(CI$dbh\fI\fR) and \fBstatement handles\fR (from now on called\&\fIsth\fR or \fI\f(CI$sth\fI\fR)..PPThe prototype of \f(CW\*(C`DBI::_new_drh()\*(C'\fR is.PP.Vb 1\& $drh = DBI::_new_drh($class, $public_attrs, $private_attrs);.Ve.PPwith the following arguments:.ie n .IP "\fI\fI$class\fI\fR" 4.el .IP "\fI\f(CI$class\fI\fR" 4.IX Item "$class"is typically the class for your driver, (for example, \*(L"DBD::File::dr\*(R"),passed as the first argument to the \f(CW\*(C`driver()\*(C'\fR method..ie n .IP "\fI\fI$public_attrs\fI\fR" 4.el .IP "\fI\f(CI$public_attrs\fI\fR" 4.IX Item "$public_attrs"is a hash ref to attributes like \fIName\fR, \fIVersion\fR, and \fIAttribution\fR.These are processed and used by \fB\s-1DBI\s0\fR. You had better not make anyassumptions about them nor should you add private attributes here..ie n .IP "\fI\fI$private_attrs\fI\fR" 4.el .IP "\fI\f(CI$private_attrs\fI\fR" 4.IX Item "$private_attrs"This is another (optional) hash ref with your private attributes.\&\fB\s-1DBI\s0\fR will store them and otherwise leave them alone..PPThe \f(CW\*(C`DBI::_new_drh()\*(C'\fR method and the \f(CW\*(C`driver()\*(C'\fR method both return \f(CW\*(C`undef\*(C'\fRfor failure (in which case you must look at \fI\f(CI$DBI::err\fI\fR and \fI\f(CI$DBI::errstr\fI\fRfor the failure information, because you have no driver handle to use)..PPUsing \fIinstall_method()\fR to expose driver-private methods.IX Subsection "Using install_method() to expose driver-private methods".PP.Vb 1\& DBD::Foo::db\->install_method($method_name, \e%attr);.Ve.PPInstalls the driver-private method named by \f(CW$method_name\fR into the\&\s-1DBI\s0 method dispatcher so it can be called directly, avoiding theneed to use the \fIfunc()\fR method..PPIt is called as a static method on the driver class to which themethod belongs. The method name must begin with the correspondingregistered driver-private prefix. For example, for DBD::Oracle\&\f(CW$method_name\fR must being with '\f(CW\*(C`ora_\*(C'\fR', and for DBD::AnyData itmust begin with '\f(CW\*(C`ad_\*(C'\fR'..PPThe attributes can be used to provide fine control over how the \s-1DBI\s0dispatcher 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).PPMethods installed using install_method default to the standard errorhandling behaviour for \s-1DBI\s0 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 \fIfunc()\fR..PPNote for driver authors: The DBD::Foo::xx\->install_method call won'twork until the class-hierarchy has been setup. Normally the \s-1DBI\s0looks after that just after the driver is loaded. This means\&\fIinstall_method()\fR 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 \fIsetup_driver()\fR method:.PP.Vb 1\& DBI\->setup_driver(\*(AqDBD::Foo\*(Aq);.Ve.PPbefore using \fIinstall_method()\fR..PPThe \s-1CLONE\s0 special subroutine.IX Subsection "The CLONE special subroutine".PPAlso needed here, in the \fBDBD::Driver\fR package, is a \f(CW\*(C`CLONE()\*(C'\fR methodthat will be called by perl when an intrepreter is cloned. All your\&\f(CW\*(C`CLONE()\*(C'\fR method needs to do, currently, is clear the cached \fI\f(CI$drh\fI\fR sothe new interpreter won't start using the cached \fI\f(CI$drh\fI\fR from the oldinterpreter:.PP.Vb 3\& sub CLONE {\& undef $drh;\& }.Ve.PPSee <http://search.cpan.org/dist/perl/pod/perlmod.pod#Making_your_module_threadsafe>for details..PP\fIThe DBD::Driver::dr package\fR.IX Subsection "The DBD::Driver::dr package".PPThe next lines of code look as follows:.PP.Vb 1\& package DBD::Driver::dr; # ====== DRIVER ======\&\& $DBD::Driver::dr::imp_data_size = 0;.Ve.PPNote that no \fI\f(CI@ISA\fI\fR is needed here, or for the other \fBDBD::Driver::*\fRclasses, because the \fB\s-1DBI\s0\fR takes care of that for you when the driver isloaded..PP.Vb 2\& *FIX ME* Explain what the imp_data_size is, so that implementors aren\*(Aqt\& practicing cargo\-cult programming..Ve.PPThe database handle constructor.IX Subsection "The database handle constructor".PPThe database handle constructor is the driver's (hence the changednamespace) \f(CW\*(C`connect()\*(C'\fR method:.PP.Vb 3\& 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\*(Aqs legal to\& # \*(Aqdie\*(Aq 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 \*(Aq=\*(Aq, $var, 2;\& return $drh\->set_err($DBI::stderr, "Can\*(Aqt parse DSN part \*(Aq$var\*(Aq")\& unless defined $attr_value;\&\& # add driver prefix to attribute name if it doesn\*(Aqt 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\*(Aqve connected\& $attr\->{$attr_name} = $attr_value;\& }\&\& # Get the attributes we\*(Aqll 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 \*(Aq$dr_dsn\*(Aq");\& my $host = delete $attr\->{drv_host} || \*(Aqlocalhost\*(Aq;\& 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\*(Aqt connect to $dr_dsn: ...");\&\& # create a \*(Aqblank\*(Aq dbh (call superclass constructor)\& my ($outer, $dbh) = DBI::_new_dbh($drh, { Name => $dr_dsn });\&\& $dbh\->STORE(\*(AqActive\*(Aq, 1 );\& $dbh\->{drv_connection} = $connection;\&\& return $outer;\& }.Ve.PPThis is mostly the same as in the \fIdriver handle constructor\fR above.The arguments are described in \s-1DBI\s0..PPThe constructor \f(CW\*(C`DBI::_new_dbh()\*(C'\fR is called, returning a database handle.The constructor's prototype is:.PP.Vb 1\& ($outer, $inner) = DBI::_new_dbh($drh, $public_attr, $private_attr);.Ve.PPwith similar arguments to those in the \fIdriver handle constructor\fR,except that the \fI\f(CI$class\fI\fR is replaced by \fI\f(CI$drh\fI\fR. The \fIName\fR attributeis a standard \fB\s-1DBI\s0\fR attribute (see \*(L"Database Handle Attributes\*(R" in \s-1DBI\s0)..PPIn scalar context, only the outer handle is returned..PPNote the use of the \f(CW\*(C`STORE()\*(C'\fR method for setting the \fIdbh\fR 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..PPBecause 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..PPHowever, some attribute values, such as those handled by the \fB\s-1DBI\s0\fR like\&\fIPrintError\fR, don't actually exist in the hash and must be read via\&\f(CW\*(C`$h\->FETCH($attrib)\*(C'\fR and set via \f(CW\*(C`$h\->STORE($attrib, $value)\*(C'\fR.If in any doubt, use these methods..PPThe \fIdata_sources()\fR method.IX Subsection "The data_sources() method".PPThe \f(CW\*(C`data_sources()\*(C'\fR method must populate and return a list of valid datasources, prefixed with the "\fIdbi:Driver\fR" incantation that allows them tobe used in the first argument of the \f(CW\*(C`DBI\->connect()\*(C'\fR method.An example of this might be scanning the \fI\f(CI$HOME\fI/.odbcini\fR file on Unixfor \s-1ODBC\s0 data sources (DSNs)..PPAs a trivial example, consider a fixed list of data sources:.PP.Vb 11\& 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;\& }.Ve.PPThe \fIdisconnect_all()\fR method.IX Subsection "The disconnect_all() method".PPIf you need to release any resources when the driver is unloaded, youcan provide a disconnect_all method..PPOther driver handle methods
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -