📄 dbd.pm
字号:
ST(0) = dbd_ix_dr_driver_init(drh) ? &sv_yes : &sv_no;and the code in F<dbdimp.h> declares: extern int dbd_ix_dr_driver_init(SV *drh);and the code in F<dbdimp.ec> (equivalent to F<dbdimp.c>) defines: /* Formally initialize the DBD::Informix driver structure */ int dbd_ix_dr_driver(SV *drh) { D_imp_drh(drh); imp_drh->n_connections = 0; /* No active connections */ imp_drh->current_connection = 0; /* No current connection */ imp_drh->multipleconnections = (ESQLC_VERSION >= 600) ? True : False; dbd_ix_link_newhead(&imp_drh->head); /* Empty linked list of connections */ return 1; }B<DBD::Oracle> has a similar requirement but gets around it by checkingwhether the private data part of the driver handle is all zeroed out,rather than add extra functions.=backNow let's take a closer look at an excerpt from F<Oracle.pm> (revisedheavily to remove idiosyncrasies) as an example, ignoring things thatwere already discussed for pure Perl drivers.=head3 The connect methodThe connect method is the database handle constructor.You could write either of two versions of this method: either one whichtakes connection attributes (new code) and one which ignores them (oldcode only).If you ignore the connection attributes, then you omit all mention ofthe I<$auth> variable (which is a reference to a hash of attributes), andthe XS system manages the differences for you. sub connect { my ($drh, $dbname, $user, $auth, $attr) = @_; # Some database specific verifications, default settings # and the like following here. This should only include # syntax checks or similar stuff where it's legal to # 'die' in case of errors. my $dbh = DBI::_new_dbh($drh, { 'Name' => $dbname, }) or return undef; # Call the driver-specific function _login in Driver.xs file which # calls the DBMS-specific function(s) to connect to the database, # and populate internal handle data. DBD::Driver::db::_login($dbh, $dbname, $user, $auth, $attr) or return undef; $dbh; }This is mostly the same as in the pure Perl case, the exception beingthe use of the private C<_login()> callback, which is the function thatwill really connect to the database. It is implemented in F<Driver.xst>(you should not implement it) and calls C<dbd_db_login6()> fromF<dbdimp.c>. See below for details. *FIX ME* Discuss removing attributes from hash reference as an optimization to skip later calls to $dbh->STORE made by DBI->connect. *FIX ME* Discuss removing attributes in Perl code. *FIX ME* Discuss removing attributes in C code.=head3 The disconnect_all method *FIX ME* T.B.S=head3 The data_sources methodIf your C<data_sources()> method can be implemented in pure Perl, then doso because it is easier than doing it in XS code (see the section abovefor pure Perl drivers).If your C<data_sources()> method must call onto compiled functions, thenyou will need to define I<dbd_dr_data_sources> in your F<dbdimp.h> file, whichwill trigger F<Driver.xst> (in B<DBI> v1.33 or greater) to generate the XScode that calls your actual C function (see the discussion below fordetails) and you do not code anything in F<Driver.pm> to handle it.=head3 The prepare methodThe prepare method is the statement handle constructor, and most of itis not new. Like the C<connect()> method, it now has a C callback: package DBD::Driver::db; # ====== DATABASE ====== use strict; sub prepare { my ($dbh, $statement, $attribs) = @_; # create a 'blank' sth my $sth = DBI::_new_sth($dbh, { 'Statement' => $statement, }) or return undef; # Call the driver-specific function _prepare in Driver.xs file # which calls the DBMS-specific function(s) to prepare a statement # and populate internal handle data. DBD::Driver::st::_prepare($sth, $statement, $attribs) or return undef; $sth; }=head3 The execute method *FIX ME* T.B.S=head3 The fetchrow_arrayref method *FIX ME* T.B.S=head3 Other methods? *FIX ME* T.B.S=head2 Driver.xsF<Driver.xs> should look something like this: #include "Driver.h" DBISTATE_DECLARE; INCLUDE: Driver.xsi MODULE = DBD::Driver PACKAGE = DBD::Driver::dr /* Non-standard drh XS methods following here, if any. */ /* If none (the usual case), omit the MODULE line above too. */ MODULE = DBD::Driver PACKAGE = DBD::Driver::db /* Non-standard dbh XS methods following here, if any. */ /* Currently this includes things like _list_tables from */ /* DBD::mSQL and DBD::mysql. */ MODULE = DBD::Driver PACKAGE = DBD::Driver::st /* Non-standard sth XS methods following here, if any. */ /* In particular this includes things like _list_fields from */ /* DBD::mSQL and DBD::mysql for accessing metadata. */Note especially the include of F<Driver.xsi> here: B<DBI> inserts stubfunctions for almost all private methods here which will typically domuch work for you.Wherever you really have to implement something, it will call a privatefunction in F<dbdimp.c>, and this is what you have to implement.You need to set up an extra routine if your driver needs to exportconstants of its own, analogous to the SQL types available when you say: use DBI qw(:sql_types); *FIX ME* T.B.S=head2 Driver.hF<Driver.h> is very simple and the operational contents should look like this: #ifndef DRIVER_H_INCLUDED #define DRIVER_H_INCLUDED #define NEED_DBIXS_VERSION 93 /* 93 for DBI versions 1.00 to 1.51+ */ #define PERL_NO_GET_CONTEXT /* if used require DBI 1.51+ */ #include <DBIXS.h> /* installed by the DBI module */ #include "dbdimp.h" #include "dbivport.h" /* see below */ #include <dbd_xsh.h> /* installed by the DBI module */ #endif /* DRIVER_H_INCLUDED */The F<DBIXS.h> header defines most of the interesting information thatthe writer of a driver needs.The file F<dbd_xsh.h> header provides prototype declarations for theC functions that you might decide to implement. Note that you shouldnormally only define one of C<dbd_db_login()> and C<dbd_db_login6()>unless you are intent on supporting really old versions of B<DBI>(prior to B<DBI> 1.06) as well as modern versions. The only standard,B<DBI>-mandated functions that you need write are those specified in theF<dbd_xsh.h> header. You might also add extra driver-specific functionsin F<Driver.xs>.The F<dbivport.h> file should be I<copied> from the latest B<DBI> releaseinto your distribution each time you modify your driver. Its job is toallow you to enhance your code to work with the latest B<DBI> API whilestill allowing your driver to be compiled and used with older versionsof the B<DBI> (for example, when the C<DBIh_SET_ERR_CHAR()> macro was addedto B<DBI> 1.41, an emulation of it was added to F<dbivport.h>). This makesusers happy and your life easier. Always read the notes in F<dbivport.h>to check for any limitations in the emulation that you should be awareof.With B<DBI> v1.51 or better I recommend that the driver definesI<PERL_NO_GET_CONTEXT> before F<DBIXS.h> is included. This can significantlyimprove efficiency when running under a thread enabled perl. (Remember thatthe standard perl in most Linux distributions is built with threads enabled.So is ActiveState perl for Windows, and perl built for Apache mod_perl2.)If you do this there are some things to keep in mind:=over 4=item *If I<PERL_NO_GET_CONTEXT> is defined, then every function that calls the PerlAPI will need to start out with a C<dTHX;> declaration.=item *You'll know which functions need this, because the C compiler willcomplain that the undeclared identifier C<my_perl> is used if I<and only if>the perl you are using to develop and test your driver has threads enabled.=item *If you don't remember to test with a thread-enabled perl before makinga release it's likely that you'll get failure reports from users who are.=item *For driver private functions it is possible to gain even moreefficiency by replacing C<dTHX;> with C<pTHX_> prepended to theparameter list and then C<aTHX_> prepended to the argument list wherethe function is called.=backSee L<perlguts/How multiple interpreters and concurrency are supported> foradditional information about I<PERL_NO_GET_CONTEXT>.=head2 Implementation header dbdimp.hThis header file has two jobs:First it defines data structures for your private part of the handles.Second it defines macros that rename the generic names likeC<dbd_db_login()> to database specific names like C<ora_db_login()>. Thisavoids name clashes and enables use of different drivers when you workwith a statically linked perl.It also will have the important task of disabling XS methods that youdon't want to implement.Finally, the macros will also be used to select alternateimplementations of some functions. For example, the C<dbd_db_login()>function is not passed the attribute hash.Since B<DBI> v1.06, if a C<dbd_db_login6()> macro is defined (for a functionwith 6 arguments), it will be used instead with the attribute hashpassed as the sixth argument.People used to just pick Oracle's F<dbdimp.c> and use the same names,structures and types. I strongly recommend against that. At first glancethis saves time, but your implementation will be less readable. It wasjust hell when I had to separate B<DBI> specific parts, Oracle specificparts, mSQL specific parts and mysql specific parts in B<DBD::mysql>'sI<dbdimp.h> and I<dbdimp.c>. (B<DBD::mysql> was a port of B<DBD::mSQL>which was based on B<DBD::Oracle>.) [Seconded, based on the experiencetaking B<DBD::Informix> apart, even though the version inherited in 1996was only based on B<DBD::Oracle>.]This part of the driver is I<your exclusive part>. Rewrite it fromscratch, so it will be clean and short: in other words, a better pieceof code. (Of course keep an eye on other people's work.) struct imp_drh_st { dbih_drc_t com; /* MUST be first element in structure */ /* Insert your driver handle attributes here */ }; struct imp_dbh_st { dbih_dbc_t com; /* MUST be first element in structure */ /* Insert your database handle attributes here */ }; struct imp_sth_st { dbih_stc_t com; /* MUST be first element in structure */ /* Insert your statement handle attributes here */ }; /* Rename functions for avoiding name clashes; prototypes are */ /* in dbd_xst.h */ #define dbd_init drv_dr_init #define dbd_db_login6 drv_db_login #define dbd_db_do drv_db_do ... many more here ...These structures implement your private part of the handles.You I<have> to use the name C<imp_dbh_{dr|db|st}> and the first fieldI<must> be of type I<dbih_drc_t|_dbc_t|_stc_t> and I<must> be calledC<com>.You should never access these fields directly, except by using theI<DBIc_xxx()> macros below.=head2 Implementation source dbdimp.cConventionally, F<dbdimp.c> is the main implementation file (butB<DBD::Informix> calls the file F<dbdimp.ec>). This section includes ashort note on each function that is used in the F<Driver.xsi> templateand thus I<has> to be implemented.Of course, you will probably also need to implement other supportfunctions, which should usually be file static if they are placed inF<dbdimp.c>. If they are placed in other files, you need to list thosefiles in F<Makefile.PL> (and F<MANIFEST>) to handle them correctly.It is wise to adhere to a namespace convention for your functions toavoid conflicts. For example, for a driver with prefix I<drv_>, youmight call externally visible functions I<dbd_drv_xxxx>. You should alsoavoid non-constant global variables as much as possible to improve thesupport for threading.Since Perl requires support for function prototypes (ANSI or ISO orStandard C), you should write your code using function prototypes too.It is possible to use either the unmapped names such as C<dbd_init()> orthe mapped names such as C<dbd_ix_dr_init()> in the F<dbdimp.c> file.B<DBD::Informix> uses the mapped names which makes it easier to identifywhere to look for linkage problems at runtime (which will report errorsusing the mapped names).Most other drivers, and in particular B<DBD::Oracle>, use the unmappednames in the source code which makes it a little easier to compare codebetween drivers and eases discussions on the I<dbi-dev> mailing list.The majority of the code fragments here will use the unmapped names.Ultimately, you should provide implementations for most fo the functionslisted in the F<dbd_xsh.h> header. The exceptions are optional functions(such as C<dbd_st_rows()>) and those functions with alternativesignatures, such as C<dbd_db_login6()> and I<dbd_db_login()>. Then youshould only implement one of the alternatives, and generally the newerone of the alternatives.=head3 The dbd_init method #include "Driver.h" DBISTATE_DECLARE; void dbd_init(dbistate_t* dbistate) { DBISTATE_INIT; /* Initialize the DBI macros */ }The C<dbd_init()> function will be called when your driver is firstloaded; the bootstrap command in C<DBD::Driver::dr::driver()> trigge
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -