📄 dbi::dbd.3
字号:
.IX Subsection "Other driver handle methods".PPIf you need any other driver handle methods, they can follow here..PPError handling.IX Subsection "Error handling".PPIt is quite likely that something fails in the connect method.With \fBDBD::File\fR for example, you might catch an error when setting thecurrent directory to something not existent by using the(driver-specific) \fIf_dir\fR attribute..PPTo report an error, you use the \f(CW\*(C`set_err()\*(C'\fR method:.PP.Vb 1\& $h\->set_err($err, $errmsg, $state);.Ve.PPThis will ensure that the error is recorded correctly and that\&\fIRaiseError\fR and \fIPrintError\fR etc are handled correctly..PPTypically you'll always use the method instance, aka your method's firstargument..PPAs \f(CW\*(C`set_err()\*(C'\fR always returns \f(CW\*(C`undef\*(C'\fR your error handling code canusually be simplified to something like this:.PP.Vb 1\& return $h\->set_err($err, $errmsg, $state) if ...;.Ve.PP\fIThe DBD::Driver::db package\fR.IX Subsection "The DBD::Driver::db package".PP.Vb 1\& package DBD::Driver::db; # ====== DATABASE ======\&\& $DBD::Driver::db::imp_data_size = 0;.Ve.PPThe statement handle constructor.IX Subsection "The statement handle constructor".PPThere's nothing much new in the statement handle constructor, whichis the \f(CW\*(C`prepare()\*(C'\fR method:.PP.Vb 3\& sub prepare\& {\& my ($dbh, $statement, @attribs) = @_;\&\& # create a \*(Aqblank\*(Aq sth\& my ($outer, $sth) = DBI::_new_sth($dbh, { Statement => $statement });\&\& $sth\->STORE(\*(AqNUM_OF_PARAMS\*(Aq, ($statement =~ tr/?//));\&\& $sth\->{drv_params} = [];\&\& return $outer;\& }.Ve.PPThis is still the same \*(-- check the arguments and call the super classconstructor \f(CW\*(C`DBI::_new_sth()\*(C'\fR. Again, in scalar context, only the outerhandle is returned. The \fIStatement\fR attribute should be cached asshown..PPNote the prefix \fIdrv_\fR 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 \fB\s-1DBI\s0\fR contains a registry ofknown driver prefixes and may one day warn about unknown attributesthat don't have a registered prefix..PPNote that we parse the statement here in order to set the attribute\&\fI\s-1NUM_OF_PARAMS\s0\fR. The technique illustrated is not very reliable; it canbe confused by question marks appearing in quoted strings, delimitedidentifiers or in \s-1SQL\s0 comments that are part of the \s-1SQL\s0 statement. Wecould set \fI\s-1NUM_OF_PARAMS\s0\fR in the \f(CW\*(C`execute()\*(C'\fR method instead becausethe \fB\s-1DBI\s0\fR specification explicitly allows a driver to defer this, but thenthe user could not call \f(CW\*(C`bind_param()\*(C'\fR..PPTransaction handling.IX Subsection "Transaction handling".PPPure Perl drivers will rarely support transactions. Thus your \f(CW\*(C`commit()\*(C'\fRand \f(CW\*(C`rollback()\*(C'\fR methods will typically be quite simple:.PP.Vb 8\& sub commit\& {\& my ($dbh) = @_;\& if ($dbh\->FETCH(\*(AqWarn\*(Aq)) {\& warn("Commit ineffective while AutoCommit is on");\& }\& 0;\& }\&\& sub rollback {\& my ($dbh) = @_;\& if ($dbh\->FETCH(\*(AqWarn\*(Aq)) {\& warn("Rollback ineffective while AutoCommit is on");\& }\& 0;\& }.Ve.PPOr even simpler, just use the default methods provided by the \fB\s-1DBI\s0\fR thatdo nothing except return \f(CW\*(C`undef\*(C'\fR..PPThe \fB\s-1DBI\s0\fR's default \f(CW\*(C`begin_work()\*(C'\fR method can be used by inheritance..PPThe \s-1\fISTORE\s0()\fR and \s-1\fIFETCH\s0()\fR methods.IX Subsection "The STORE() and FETCH() methods".PPThese methods (that we have already used, see above) are called foryou, whenever the user does a:.PP.Vb 1\& $dbh\->{$attr} = $val;.Ve.PPor, respectively,.PP.Vb 1\& $val = $dbh\->{$attr};.Ve.PPSee perltie for details on tied hash refs to understand why thesemethods are required..PPThe \fB\s-1DBI\s0\fR will handle most attributes for you, in particular attributeslike \fIRaiseError\fR or \fIPrintError\fR. All you have to do is handle yourdriver's private attributes and any attributes, like \fIAutoCommit\fR and\&\fIChopBlanks\fR, that the \fB\s-1DBI\s0\fR can't handle for you..PPA good example might look like this:.PP.Vb 10\& sub STORE\& {\& my ($dbh, $attr, $val) = @_;\& if ($attr eq \*(AqAutoCommit\*(Aq) {\& # AutoCommit is currently the only standard attribute we have\& # to consider.\& if (!$val) { die "Can\*(Aqt 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 \*(AqAutoCommit\*(Aq) { 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);\& }.Ve.PPThe \fB\s-1DBI\s0\fR 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 \f(CW\*(C`FETCH()\*(C'\fR and \f(CW\*(C`STORE()\*(C'\fRmethods unless you need extra logic/checks, beyond getting or settingthe value..PPUnless your driver documentation indicates otherwise, the return value ofthe \f(CW\*(C`STORE()\*(C'\fR method is unspecified and the caller shouldn't use that value..PPOther database handle methods.IX Subsection "Other database handle methods".PPAs with the driver package, other database handle methods may follow here.In particular you should consider a (possibly empty) \f(CW\*(C`disconnect()\*(C'\fRmethod and possibly a \f(CW\*(C`quote()\*(C'\fR method if \fB\s-1DBI\s0\fR's default isn't correct foryou. You may also need the \f(CW\*(C`type_info_all()\*(C'\fR and \f(CW\*(C`get_info()\*(C'\fR methods,as described elsewhere in this document..PPWhere reasonable use \f(CW\*(C`$h\->SUPER::foo()\*(C'\fR to call the \fB\s-1DBI\s0\fR's method insome or all cases and just wrap your custom behavior around that..PPIf 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 a\&\f(CW\*(C`parse_trace_flag()\*(C'\fR method (note that's \*(L"parse_trace_flag\*(R", singular,not \*(L"parse_trace_flags\*(R", plural)..PP.Vb 9\& sub parse_trace_flag {\& my ($h, $name) = @_;\& return 0x01000000 if $name eq \*(Aqfoo\*(Aq;\& return 0x02000000 if $name eq \*(Aqbar\*(Aq;\& return 0x04000000 if $name eq \*(Aqbaz\*(Aq;\& return 0x08000000 if $name eq \*(Aqboo\*(Aq;\& return 0x10000000 if $name eq \*(Aqbop\*(Aq;\& return $h\->SUPER::parse_trace_flag($name);\& }.Ve.PPAll private flag names must be lowercase, and all private flagsmust be in the top 8 of the 32 bits..PP\fIThe DBD::Driver::st package\fR.IX Subsection "The DBD::Driver::st package".PPThis package follows the same pattern the others do:.PP.Vb 1\& package DBD::Driver::st;\&\& $DBD::Driver::st::imp_data_size = 0;.Ve.PPThe \fIexecute()\fR and \fIbind_param()\fR methods.IX Subsection "The execute() and bind_param() methods".PPThis 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 \fB\s-1DBI\s0\fR methods tofunction correctly (see \*(L"Statement attributes\*(R" below)..PPWe present a simplified implementation by using the \fIdrv_params\fRattribute from above:.PP.Vb 12\& sub bind_param\& {\& my ($sth, $pNum, $val, $attr) = @_;\& my $type = (ref $attr) ? $attr\->{TYPE} : $attr;\& if ($type) {\& my $dbh = $sth\->{Database};\& $val = $dbh\->quote($sth, $type);\& }\& my $params = $sth\->{drv_params};\& $params\->[$pNum\-1] = $val;\& 1;\& }\&\& sub execute\& {\& my ($sth, @bind_values) = @_;\&\& # start of by finishing any previous execution if still active\& $sth\->finish if $sth\->FETCH(\*(AqActive\*(Aq);\&\& my $params = (@bind_values) ?\& \e@bind_values : $sth\->{drv_params};\& my $numParam = $sth\->FETCH(\*(AqNUM_OF_PARAMS\*(Aq);\& return $sth\->set_err($DBI::stderr, "Wrong number of parameters")\& if @$params != $numParam;\& my $statement = $sth\->{\*(AqStatement\*(Aq};\& for (my $i = 0; $i < $numParam; $i++) {\& $statement =~ s/?/$params\->[$i]/; # XXX doesn\*(Aqt deal with quoting etc!\& }\& # Do anything ... we assume that an array ref of rows is\& # created and store it:\& $sth\->{\*(Aqdrv_data\*(Aq} = $data;\& $sth\->{\*(Aqdrv_rows\*(Aq} = @$data; # number of rows\& $sth\->STORE(\*(AqNUM_OF_FIELDS\*(Aq) = $numFields;\& $sth\->{Active} = 1;\& @$data || \*(Aq0E0\*(Aq;\& }.Ve.PPThere are a number of things you should note here..PPWe initialize the \fI\s-1NUM_OF_FIELDS\s0\fR and \fIActive\fR attributes here,because they are essential for \f(CW\*(C`bind_columns()\*(C'\fR to work..PPWe use attribute \f(CW\*(C`$sth\->{Statement}\*(C'\fR which we createdwithin \f(CW\*(C`prepare()\*(C'\fR. The attribute \f(CW\*(C`$sth\->{Database}\*(C'\fR, which isnothing else than the \fIdbh\fR, was automatically created by \fB\s-1DBI\s0\fR..PPFinally, note that (as specified in the \fB\s-1DBI\s0\fR specification) we return thestring \f(CW\*(Aq0E0\*(Aq\fR instead of the number 0, so that the result tests true butequal to zero..PP.Vb 1\& $sth\->execute() or die $sth\->errstr;.Ve.PPThe \fIexecute_array()\fR, \fIexecute_for_fetch()\fR and \fIbind_param_array()\fR methods.IX Subsection "The execute_array(), execute_for_fetch() and bind_param_array() methods".PPIn general, \s-1DBD\s0's only need to implement \f(CW\*(C`execute_for_fetch()\*(C'\fR and\&\f(CW\*(C`bind_param_array\*(C'\fR. \s-1DBI\s0's default \f(CW\*(C`execute_array()\*(C'\fR will invoke the\&\s-1DBD\s0's \f(CW\*(C`execute_for_fetch()\*(C'\fR as needed..PPThe following sequence describes the interaction between\&\s-1DBI\s0 \f(CW\*(C`execute_array\*(C'\fR and a \s-1DBD\s0's \f(CW\*(C`execute_for_fetch\*(C'\fR:.IP "1." 4App calls \f(CW\*(C`$sth\->execute_array(\e%attrs, @array_of_arrays)\*(C'\fR.IP "2." 4If \f(CW@array_of_arrays\fR was specified, \s-1DBI\s0 processes \f(CW@array_of_arrays\fR by calling\&\s-1DBD\s0's \f(CW\*(C`bind_param_array()\*(C'\fR. Alternately, App may have directly called\&\f(CW\*(C`bind_param_array()\*(C'\fR.IP "3." 4\&\s-1DBD\s0 validates and binds each array.IP "4." 4\&\s-1DBI\s0 retrieves the validated param arrays from \s-1DBD\s0's ParamArray attribute.IP "5." 4\&\s-1DBI\s0 calls \s-1DBD\s0's \f(CW\*(C`execute_for_fetch($fetch_tuple_sub, \e@tuple_status)\*(C'\fR,where \f(CW&$fetch_tuple_sub\fR is a closure to iterate over thereturned ParamArray values, and \f(CW\*(C`\e@tuple_status\*(C'\fR is an array to receivethe disposition status of each tuple..IP "6." 4\&\s-1DBD\s0 iteratively calls \f(CW&$fetch_tuple_sub\fR to retrieve parameter tuplesto be added to its bulk database operation/request..IP "7." 4when \s-1DBD\s0 reaches the limit of tuples it can handle in a single databaseoperation/request, or the \f(CW&$fetch_tuple_sub\fR indicates no moretuples by returning undef, the \s-1DBD\s0 executes the bulk operation, andreports the disposition of each tuple in \e@tuple_status..IP "8." 4\&\s-1DBD\s0 repeats steps 6 and 7 until all tuples are processed..PPE.g., here's the essence of DBD::Oracle's execute_for_fetch:.PP
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -