📄 dbd.html
字号:
<pre>
<span class="comment"># Some database specific verifications, default settings</span>
<span class="comment"># and the like can go here. This should only include</span>
<span class="comment"># syntax checks or similar stuff where it's legal to</span>
<span class="comment"># 'die' in case of errors.</span>
<span class="comment"># For example, many database packages requires specific</span>
<span class="comment"># environment variables to be set; this could be where you</span>
<span class="comment"># validate that they are set, or default them if they are not set.</span>
</pre>
<pre>
<span class="keyword">my</span> <span class="variable">$driver_prefix</span> <span class="operator">=</span> <span class="string">"drv_"</span><span class="operator">;</span> <span class="comment"># the assigned prefix for this driver</span>
</pre>
<pre>
<span class="comment"># Process attributes from the DSN; we assume ODBC syntax</span>
<span class="comment"># here, that is, the DSN looks like var1=val1;...;varN=valN</span>
<span class="keyword">foreach</span> <span class="keyword">my</span> <span class="variable">$var</span> <span class="operator">(</span> <span class="keyword">split</span> <span class="regex">/;/</span><span class="operator">,</span> <span class="variable">$dr_dsn</span> <span class="operator">)</span> <span class="operator">{</span>
<span class="keyword">my</span> <span class="operator">(</span><span class="variable">$attr_name</span><span class="operator">,</span> <span class="variable">$attr_value</span><span class="operator">)</span> <span class="operator">=</span> <span class="keyword">split</span> <span class="string">'='</span><span class="operator">,</span> <span class="variable">$var</span><span class="operator">,</span> <span class="number">2</span><span class="operator">;</span>
<span class="keyword">return</span> <span class="variable">$drh</span><span class="operator">-></span><span class="variable">set_err</span><span class="operator">(</span><span class="number">1</span><span class="operator">,</span> <span class="string">"Can't parse DSN part '$var'"</span><span class="operator">)</span>
<span class="keyword">unless</span> <span class="keyword">defined</span> <span class="variable">$attr_value</span><span class="operator">;</span>
</pre>
<pre>
<span class="comment"># add driver prefix to attribute name if it doesn't have it already</span>
<span class="variable">$attr_name</span> <span class="operator">=</span> <span class="variable">$driver_prefix</span><span class="operator">.</span><span class="variable">$attr_name</span>
<span class="keyword">unless</span> <span class="variable">$attr_name</span> <span class="operator">=~</span> <span class="regex">/^$driver_prefix/o</span><span class="operator">;</span>
</pre>
<pre>
<span class="comment"># Store attribute into %$attr, replacing any existing value.</span>
<span class="comment"># The DBI will STORE() these into $dbh after we've connected</span>
<span class="variable">$attr</span><span class="operator">-></span><span class="operator">{</span><span class="variable">$attr_name</span><span class="operator">}</span> <span class="operator">=</span> <span class="variable">$attr_value</span><span class="operator">;</span>
<span class="operator">}</span>
</pre>
<pre>
<span class="comment"># Get the attributes we'll use to connect.</span>
<span class="comment"># We use delete here because these no need to STORE them</span>
<span class="keyword">my</span> <span class="variable">$db</span> <span class="operator">=</span> <span class="keyword">delete</span> <span class="variable">$attr</span><span class="operator">-></span><span class="operator">{</span><span class="string">drv_database</span><span class="operator">}</span> <span class="operator">||</span> <span class="keyword">delete</span> <span class="variable">$attr</span><span class="operator">-></span><span class="operator">{</span><span class="string">drv_db</span><span class="operator">}</span>
<span class="keyword">or</span> <span class="keyword">return</span> <span class="variable">$drh</span><span class="operator">-></span><span class="variable">set_err</span><span class="operator">(</span><span class="number">1</span><span class="operator">,</span> <span class="string">"No database name given in DSN '$dr_dsn'"</span><span class="operator">);</span>
<span class="keyword">my</span> <span class="variable">$host</span> <span class="operator">=</span> <span class="keyword">delete</span> <span class="variable">$attr</span><span class="operator">-></span><span class="operator">{</span><span class="string">drv_host</span><span class="operator">}</span> <span class="operator">||</span> <span class="string">'localhost'</span><span class="operator">;</span>
<span class="keyword">my</span> <span class="variable">$port</span> <span class="operator">=</span> <span class="keyword">delete</span> <span class="variable">$attr</span><span class="operator">-></span><span class="operator">{</span><span class="string">drv_port</span><span class="operator">}</span> <span class="operator">||</span> <span class="number">123456</span><span class="operator">;</span>
</pre>
<pre>
<span class="comment"># Assume you can attach to your database via drv_connect:</span>
<span class="keyword">my</span> <span class="variable">$connection</span> <span class="operator">=</span> <span class="variable">drv_connect</span><span class="operator">(</span><span class="variable">$db</span><span class="operator">,</span> <span class="variable">$host</span><span class="operator">,</span> <span class="variable">$port</span><span class="operator">,</span> <span class="variable">$user</span><span class="operator">,</span> <span class="variable">$auth</span><span class="operator">)</span>
<span class="keyword">or</span> <span class="keyword">return</span> <span class="variable">$drh</span><span class="operator">-></span><span class="variable">set_err</span><span class="operator">(</span><span class="number">1</span><span class="operator">,</span> <span class="string">"Can't connect to $dr_dsn: ..."</span><span class="operator">);</span>
</pre>
<pre>
<span class="comment"># create a 'blank' dbh (call superclass constructor)</span>
<span class="keyword">my</span> <span class="operator">(</span><span class="variable">$outer</span><span class="operator">,</span> <span class="variable">$dbh</span><span class="operator">)</span> <span class="operator">=</span> <span class="variable">DBI::_new_dbh</span><span class="operator">(</span><span class="variable">$drh</span><span class="operator">,</span> <span class="operator">{</span> <span class="string">Name</span> <span class="operator">=></span> <span class="variable">$dr_dsn</span> <span class="operator">}</span><span class="operator">);</span>
</pre>
<pre>
<span class="variable">$dbh</span><span class="operator">-></span><span class="variable">STORE</span><span class="operator">(</span><span class="string">'Active'</span><span class="operator">,</span> <span class="number">1</span> <span class="operator">);</span>
<span class="variable">$dbh</span><span class="operator">-></span><span class="operator">{</span><span class="string">drv_connection</span><span class="operator">}</span> <span class="operator">=</span> <span class="variable">$connection</span><span class="operator">;</span>
</pre>
<pre>
<span class="keyword">return</span> <span class="variable">$outer</span><span class="operator">;</span>
<span class="operator">}</span>
</pre>
<p>The Name attribute is a standard DBI attribute.</p>
<p>This is mostly the same as in the <em>driver handle constructor</em> above.
The arguments are described in the DBI man page.
See <a href="../../lib/DBI.html">the DBI manpage</a>.
The constructor _new_dbh is called, returning a database handle.
The constructor's prototype is:</p>
<pre>
<span class="operator">(</span><span class="variable">$outer</span><span class="operator">,</span> <span class="variable">$inner</span><span class="operator">)</span> <span class="operator">=</span> <span class="variable">DBI::_new_dbh</span><span class="operator">(</span><span class="variable">$drh</span><span class="operator">,</span> <span class="variable">$public_attr</span><span class="operator">,</span> <span class="variable">$private_attr</span><span class="operator">);</span>
</pre>
<p>with similar arguments to those in the <em>driver handle constructor</em>,
except that the <a href="#item__class"><code>$class</code></a> is replaced by <code>$drh</code>.</p>
<p>In scalar context, only the outer handle is returned.</p>
<p>Note the use of the <em>STORE</em> method for setting the dbh attributes.
That's because within the driver code, the handle object you have is
the 'inner' handle of a tied hash, not the outer handle that the
users of your driver have.</p>
<p>Because you have the inner handle, tie magic doesn't get invoked
when you get or set values in the hash. This is often very handy for
speed when you want to get or set simple non-special driver-specific
attributes.</p>
<p>However, some attribute values, such as those handled by the DBI
like PrintError, don't actually exist in the hash and must be
read via $h-><code>FETCH($attrib)</code> and set via $h->STORE($attrib, $value).
If in any doubt, use these methods.</p>
<p>
</p>
<h4><a name="the_data_sources_method">The data_sources method</a></h4>
<p>The data_sources method must populate and return a list of valid data
sources, prefixed with the "dbi:Driver" incantation that allows them to
be used in the first argument of the <a href="../../lib/Pod/perlfunc.html#item_connect"><code>DBI->connect</code></a> method.
An example of this might be scanning the <em>$HOME/.odbcini</em> file on Unix
for ODBC data sources (DSNs).
As a trivial example, consider a fixed list of data sources:</p>
<pre>
<span class="keyword">sub</span><span class="variable"> data_sources
</span><span class="operator">{</span>
<span class="keyword">my</span><span class="operator">(</span><span class="variable">$drh</span><span class="operator">,</span> <span class="variable">$attr</span><span class="operator">)</span> <span class="operator">=</span> <span class="variable">@_</span><span class="operator">;</span>
<span class="keyword">my</span><span class="operator">(</span><span class="variable">@list</span><span class="operator">)</span> <span class="operator">=</span> <span class="operator">();</span>
<span class="comment"># You need more sophisticated code than this to set @list...</span>
<span class="keyword">push</span> <span class="variable">@list</span><span class="operator">,</span> <span class="string">"dbi:Driver:abc"</span><span class="operator">;</span>
<span class="keyword">push</span> <span class="variable">@list</span><span class="operator">,</span> <span class="string">"dbi:Driver:def"</span><span class="operator">;</span>
<span class="keyword">push</span> <span class="variable">@list</span><span class="operator">,</span> <span class="string">"dbi:Driver:ghi"</span><span class="operator">;</span>
<span class="comment"># End of code to set @list</span>
<span class="keyword">return</span> <span class="variable">@list</span><span class="operator">;</span>
<span class="operator">}</span>
</pre>
<p>
</p>
<h4><a name="error_handling">Error handling</a></h4>
<p>It is quite likely that something fails in the connect method.
With DBD::File for example, you might catch an error when setting the
current directory to something not existent by using the
(driver-specific) f_dir attribute.</p>
<p>To report an error, you use the <code>set_err</code> method:</p>
<pre>
<span class="variable">$h</span><span class="operator">-></span><span class="variable">set_err</span><span class="operator">(</span><span class="variable">$err</span><span class="operator">,</span> <span class="variable">$errmsg</span><span class="operator">,</span> <span class="variable">$state</span><span class="operator">);</span>
</pre>
<p>This will ensure that the error is recorded correctly and that
RaiseError and PrintError etc are handled correctly.
Typically you'll
always use the method instance, aka your method's first argument.</p>
<p>As set_err always returns undef your error handling code can
usually be simplified to something like this:</p>
<pre>
<span class="keyword">return</span> <span class="variable">$h</span><span class="operator">-></span><span class="variable">set_err</span><span class="operator">(</span><span class="variable">$err</span><span class="operator">,</span> <span class="variable">$errmsg</span><span class="operator">,</span> <span class="variable">$state</span><span class="operator">)</span> <span class="keyword">if</span> <span class="operator">...;</span>
</pre>
<p>
</p>
<h4><a name="the_disconnect_all_method">The disconnect_all method</a></h4>
<p>If you need to release any resources when the driver is unloaded, you
can provide a disconnect_all method.</p>
<p>
</p>
<h4><a name="other_driver_handle_methods">Other driver handle methods</a></h4>
<p>If you need any other driver handle methods, they can follow here.</p>
<p>
</p>
<h3><a name="the_dbd__driver__db_package">The DBD::Driver::db package</a></h3>
<p>
</p>
<h4><a name="the_statement_handle_constructor">The statement handle constructor</a></h4>
<p>There's nothing much new in the statement handle constructor.</p>
<pre>
<span class="keyword">package</span> <span class="variable">DBD::Driver::db</span><span class="operator">;</span> <span class="comment"># ====== DATABASE ======</span>
</pre>
<pre>
<span class="variable">$DBD::Driver::db::imp_data_size</span> <span class="operator">=</span> <span class="number">0</span><span class="operator">;</span>
</pre>
<pre>
<span class="keyword">sub</span><span class="variable"> prepare
</span><span class="operator">{</span>
<span class="keyword">my</span> <span class="operator">(</span><span class="variable">$dbh</span><span class="operator">,</span> <span class="variable">$statement</span><span class="operator">,</span> <span class="variable">@attribs</span><span class="operator">)</span> <span class="operator">=</span> <span class="variable">@_</span><span class="operator">;</span>
</pre>
<pre>
<span class="comment"># create a 'blank' sth</span>
<span class="keyword">my</span> <span class="operator">(</span><span class="variable">$outer</span><span class="operator">,</span> <span class="variable">$sth</span><span class="operator">)</span> <span class="operator">=</span> <span class="variable">DBI::_new_sth</span><span class="operator">(</span><span class="variable">$dbh</span><span class="operator">,</span> <span class="operator">{</span> <span class="string">Statement</span> <span class="operator">=></span> <span class="variable">$statement</span> <span class="operator">}</span><span class="operator">);</span>
</pre>
<pre>
<span class="variable">$sth</span><span class="operator">-></span><span class="variable">STORE</span><span class="operator">(</span><span class="string">'NUM_OF_PARAMS'</span><span class="operator">,</span> <span class="operator">(</span><span class="variable">$statement</span> <span class="operator">=~</span> <span class="regex">tr/?//</span><span class="operator">));</span>
</pre>
<pre>
<span class="variable">$sth</span><span class="operator">-></span><span class="operator">{</span><span class="string">drv_params</span><span class="operator">}</span> <span class="operator">=</span> <span class="operator">[]</span><span class="operator">;</span>
</pre>
<pre>
<span class="keyword">return</span> <span class="variable">$outer</span><span class="operator">;</span>
<span class="operator">}</span>
</pre>
<p>This is still the same: check the arguments and call the super class
constructor <em>DBI::_new_sth</em>.
Again, in scalar context, only the outer handle is returned.
The <code>Statement</code> attribute should be cached as shown.</p>
<p>Note the prefix <em>drv_</em> in the attribute names: it is required that
all your private attributes use a lowercase prefix unique to your driver.
The DBI contains a registry of known driver prefixes and may one day
warn about unknown attributes that don't have a registered prefix.</p>
<p>Note that we parse the statement here in order to set the attribute
<em>NUM_OF_PARAMS</em>.
The technique illustrated is not very reliable; it can be confused by
question marks appearing in quoted strings, delimited identifiers or in
SQL comments that are part of the SQL statement.
We could set <em>NUM_OF_PARAMS</em> in the <em>execute</em> method instead because
the DBI specification explicitly allows a driver to defer this, but then
the user could not call <em>bind_param</em>.</p>
<p>
</p>
<h4><a name="transaction_handling">Transaction handling</a></h4>
<p>Pure Perl drivers will rarely support transactions. Thus your <em>commit</em>
and <em>rollback</em> methods will typically be quite simple:</p>
<pre>
<span class="keyword">sub</span><span class="variable"> commit
</span><span class="operator">{</span>
<span class="keyword">my</span> <span class="operator">(</span><span class="variable">$dbh</span><span class="operator">)</span> <span class="operator">=</span> <span class="variable">@_</span><span class="operator">;</span>
<span class="keyword">if</span> <span class="operator">(</span><span class="variable">$dbh</span><span class="operator">-></span><span class="variable">FETCH</span><span class="operator">(</span><span class="string">'Warn'</span><span class="operator">))</span> <span class="operator">{</span>
<span class="keyword">warn</span><span class="operator">(</span><span class="string">"Commit ineffective while AutoCommit is on"</span><span class="operator">);</span>
<span class="operator">}</span>
<span class="number">0</span><span class="operator">;</span>
<span class="operator">}</span>
</pre>
<pre>
<span class="keyword">sub</span><span class="variable"> rollback </span><span class="operator">{</span>
<span class="keyword">my</span> <span class="operator">(</span><span class="variable">$dbh</span><span class="operator">)</span> <span class="operator">=</span> <span class="variable">@_</span><span class="operator">;</span>
<span class="keyword">if</span> <span class="operator">(</span><span class="variable">$dbh</span><span class="operator">-></span><span class="variable">FETCH</span><span class="operator">(</span><span class="string">'Warn'</span><span class="operator">))</span> <span class="operator">{</span>
<span class="keyword">warn</span><span class="operator">(</span><span class="string">"Rollback ineffective while AutoCommit is on"</span><span class="operator">);</span>
<span class="operator">}</span>
<span class="number">0</span><span class="operator">;</span>
<span class="operator">}</span>
</pre>
<p>Or even simpler, just use the default methods provided by the DBI that
do nothing except return undef.</p>
<p>The DBI's default begin_work method can be used by inheritance.</p>
<p>
</p>
<h4><a name="the_store_and_fetch_methods">The STORE and FETCH methods</a></h4>
<p>These methods (that we have already used, see above) are called for
you, whenever the user does a:</p>
<pre>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -