1DBI::DBD::SqlEngine::HoUwsTeor(3C)ontributed Perl DocumeDnBtIa:t:iDoBnD::SqlEngine::HowTo(3)
2
3
4

NAME

6       DBI::DBD::SqlEngine::HowTo - Guide to create DBI::DBD::SqlEngine based
7       driver
8

SYNOPSIS

10         perldoc DBI::DBD::SqlEngine::HowTo
11         perldoc DBI
12         perldoc DBI::DBD
13         perldoc DBI::DBD::SqlEngine::Developers
14         perldoc SQL::Eval
15         perldoc DBI::DBD::SqlEngine
16         perldoc DBI::DBD::SqlEngine::HowTo
17         perldoc SQL::Statement::Embed
18

DESCRIPTION

20       This document provides a step-by-step guide, how to create a new
21       "DBI::DBD::SqlEngine" based DBD. It expects that you carefully read the
22       DBI documentation and that you're familiar with DBI::DBD and had read
23       and understood DBD::ExampleP.
24
25       This document addresses experienced developers who are really sure that
26       they need to invest time when writing a new DBI Driver. Writing a DBI
27       Driver is neither a weekend project nor an easy job for hobby coders
28       after work. Expect one or two man-month of time for the first start.
29
30       Those who are still reading, should be able to sing the rules of
31       "CREATING A NEW DRIVER" in DBI::DBD.
32

CREATING DRIVER CLASSES

34       Do you have an entry in DBI's DBD registry? For this guide, a prefix of
35       "foo_" is assumed.
36
37   Sample Skeleton
38           package DBD::Foo;
39
40           use strict;
41           use warnings;
42           use vars qw($VERSION);
43           use base qw(DBI::DBD::SqlEngine);
44
45           use DBI ();
46
47           $VERSION = "0.001";
48
49           package DBD::Foo::dr;
50
51           use vars qw(@ISA $imp_data_size);
52
53           @ISA = qw(DBI::DBD::SqlEngine::dr);
54           $imp_data_size = 0;
55
56           package DBD::Foo::db;
57
58           use vars qw(@ISA $imp_data_size);
59
60           @ISA = qw(DBI::DBD::SqlEngine::db);
61           $imp_data_size = 0;
62
63           package DBD::Foo::st;
64
65           use vars qw(@ISA $imp_data_size);
66
67           @ISA = qw(DBI::DBD::SqlEngine::st);
68           $imp_data_size = 0;
69
70           package DBD::Foo::Statement;
71
72           use vars qw(@ISA);
73
74           @ISA = qw(DBI::DBD::SqlEngine::Statement);
75
76           package DBD::Foo::Table;
77
78           use vars qw(@ISA);
79
80           @ISA = qw(DBI::DBD::SqlEngine::Table);
81
82           1;
83
84       Tiny, eh? And all you have now is a DBD named foo which will is able to
85       deal with temporary tables, as long as you use SQL::Statement. In
86       DBI::SQL::Nano environments, this DBD can do nothing.
87
88   Deal with own attributes
89       Before we start doing usable stuff with our DBI driver, we need to
90       think about what we want to do and how we want to do it.
91
92       Do we need tunable knobs accessible by users? Do we need status
93       information? All this is handled in attributes of the database handles
94       (be careful when your DBD is running "behind" a DBD::Gofer proxy).
95
96       How come the attributes into the DBD and how are they fetchable by the
97       user? Good question, but you should know because you've read the DBI
98       documentation.
99
100       "DBI::DBD::SqlEngine::db::FETCH" and "DBI::DBD::SqlEngine::db::STORE"
101       taking care for you - all they need to know is which attribute names
102       are valid and mutable or immutable. Tell them by adding
103       "init_valid_attributes" to your db class:
104
105           sub init_valid_attributes
106           {
107               my $dbh = $_[0];
108
109               $dbh->SUPER::init_valid_attributes ();
110
111               $dbh->{foo_valid_attrs} = {
112                   foo_version         => 1,   # contains version of this driver
113                   foo_valid_attrs     => 1,   # contains the valid attributes of foo drivers
114                   foo_readonly_attrs  => 1,   # contains immutable attributes of foo drivers
115                   foo_bar             => 1,   # contains the bar attribute
116                   foo_baz             => 1,   # contains the baz attribute
117                   foo_manager         => 1,   # contains the manager of the driver instance
118                   foo_manager_type    => 1,   # contains the manager class of the driver instance
119               };
120               $dbh->{foo_readonly_attrs} = {
121                   foo_version         => 1,   # ensure no-one modifies the driver version
122                   foo_valid_attrs     => 1,   # do not permit to add more valid attributes ...
123                   foo_readonly_attrs  => 1,   # ... or make the immutable mutable
124                   foo_manager         => 1,   # manager is set internally only
125               };
126
127               return $dbh;
128           }
129
130       Woooho - but now the user cannot assign new managers? This is intended,
131       overwrite "STORE" to handle it!
132
133           sub STORE ($$$)
134           {
135               my ( $dbh, $attrib, $value ) = @_;
136
137               $dbh->SUPER::STORE( $attrib, $value );
138
139               # we're still alive, so no exception is thrown ...
140               # by DBI::DBD::SqlEngine::db::STORE
141               if ( $attrib eq "foo_manager_type" )
142               {
143                   $dbh->{foo_manager} = $dbh->{foo_manager_type}->new();
144                   # ... probably correct some states based on the new
145                   # foo_manager_type - see DBD::Sys for an example
146               }
147           }
148
149       But ... my driver runs without a manager until someone first assignes a
150       "foo_manager_type". Well, no - there're two places where you can
151       initialize defaults:
152
153           sub init_default_attributes
154           {
155               my ($dbh, $phase) = @_;
156
157               $dbh->SUPER::init_default_attributes($phase);
158
159               if( 0 == $phase )
160               {
161                   # init all attributes which have no knowledge about
162                   # user settings from DSN or the attribute hash
163                   $dbh->{foo_manager_type} = "DBD::Foo::Manager";
164               }
165               elsif( 1 == $phase )
166               {
167                   # init phase with more knowledge from DSN or attribute
168                   # hash
169                   $dbh->{foo_manager} = $dbh->{foo_manager_type}->new();
170               }
171
172               return $dbh;
173           }
174
175       So far we can prevent the users to use our database driver as data
176       storage for anything and everything. We care only about the real
177       important stuff for peace on earth and alike attributes. But in fact,
178       the driver still can't do anything. It can do less than nothing -
179       meanwhile it's not a stupid storage area anymore.
180
181   User comfort
182       "DBI::DBD::SqlEngine" since 0.05 consolidates all persistent meta data
183       of a table into a single structure stored in "$dbh->{sql_meta}". While
184       DBI::DBD::SqlEngine provides only readonly access to this structure,
185       modifications are still allowed.
186
187       Primarily DBI::DBD::SqlEngine provides access via the setters
188       "get_sql_engine_meta", "get_single_table_meta",
189       "set_single_table_meta", "set_sql_engine_meta" and
190       "clear_sql_engine_meta". Those methods are easily accessible by the
191       users via the "$dbh->func ()" interface provided by DBI. Well, many
192       users don't feel comfortize when calling
193
194           # don't require extension for tables cars
195           $dbh->func ("cars", "f_ext", ".csv", "set_sql_engine_meta");
196
197       DBI::DBD::SqlEngine will inject a method into your driver to increase
198       the user comfort to allow:
199
200           # don't require extension for tables cars
201           $dbh->foo_set_meta ("cars", "f_ext", ".csv");
202
203       Better, but here and there users likes to do:
204
205           # don't require extension for tables cars
206           $dbh->{foo_tables}->{cars}->{f_ext} = ".csv";
207
208       This interface is provided when derived DBD's define following in
209       "init_valid_attributes" (re-capture "Deal with own attributes"):
210
211           sub init_valid_attributes
212           {
213               my $dbh = $_[0];
214
215               $dbh->SUPER::init_valid_attributes ();
216
217               $dbh->{foo_valid_attrs} = {
218                   foo_version         => 1,   # contains version of this driver
219                   foo_valid_attrs     => 1,   # contains the valid attributes of foo drivers
220                   foo_readonly_attrs  => 1,   # contains immutable attributes of foo drivers
221                   foo_bar             => 1,   # contains the bar attribute
222                   foo_baz             => 1,   # contains the baz attribute
223                   foo_manager         => 1,   # contains the manager of the driver instance
224                   foo_manager_type    => 1,   # contains the manager class of the driver instance
225                   foo_meta            => 1,   # contains the public interface to modify table meta attributes
226               };
227               $dbh->{foo_readonly_attrs} = {
228                   foo_version         => 1,   # ensure no-one modifies the driver version
229                   foo_valid_attrs     => 1,   # do not permit to add more valid attributes ...
230                   foo_readonly_attrs  => 1,   # ... or make the immutable mutable
231                   foo_manager         => 1,   # manager is set internally only
232                   foo_meta            => 1,   # ensure public interface to modify table meta attributes are immutable
233               };
234
235               $dbh->{foo_meta} = "foo_tables";
236
237               return $dbh;
238           }
239
240       This provides a tied hash in "$dbh->{foo_tables}" and a tied hash for
241       each table's meta data in "$dbh->{foo_tables}->{$table_name}".
242       Modifications on the table meta attributes are done using the table
243       methods:
244
245           sub get_table_meta_attr { ... }
246           sub set_table_meta_attr { ... }
247
248       Both methods can adjust the attribute name for compatibility reasons,
249       e.g.  when former versions of the DBD allowed different names to be
250       used for the same flag:
251
252           my %compat_map = (
253                              abc => 'foo_abc',
254                              xyz => 'foo_xyz',
255                            );
256           __PACKAGE__->register_compat_map( \%compat_map );
257
258       If any user modification on a meta attribute needs reinitialization of
259       the meta structure (in case of "DBI::DBD::SqlEngine" these are the
260       attributes "f_file", "f_dir", "f_ext" and "f_lockfile"), inform
261       DBI::DBD::SqlEngine by doing
262
263           my %reset_on_modify = (
264                                   foo_xyz => "foo_bar",
265                                   foo_abc => "foo_bar",
266                                 );
267           __PACKAGE__->register_reset_on_modify( \%reset_on_modify );
268
269       The next access to the table meta data will force DBI::DBD::SqlEngine
270       to re-do the entire meta initialization process.
271
272       Any further action which needs to be taken can handled in
273       "table_meta_attr_changed":
274
275           sub table_meta_attr_changed
276           {
277               my ($class, $meta, $attrib, $value) = @_;
278               ...
279               $class->SUPER::table_meta_attr_changed ($meta, $attrib, $value);
280           }
281
282       This is done before the new value is set in $meta, so the attribute
283       changed handler can act depending on the old value.
284
285   Dealing with Tables
286       Let's put some life into it - it's going to be time for it.
287
288       This is a good point where a quick side step to SQL::Statement::Embed
289       will help to shorten the next paragraph. The documentation in
290       SQL::Statement::Embed regarding embedding in own DBD's works pretty
291       fine with SQL::Statement and DBI::SQL::Nano.
292
293       Second look should go to DBI::DBD::SqlEngine::Developers to get a
294       picture over the driver part of the table API. Usually there isn't much
295       to do for an easy driver.
296
297   Testing
298       Now you should have your first own DBD. Was easy, wasn't it?  But does
299       it work well? Prove it by writing tests and remember to use
300       dbd_edit_mm_attribs from DBI::DBD to ensure testing even rare cases.
301

AUTHOR

303       This guide is written by Jens Rehsack. DBI::DBD::SqlEngine is written
304       by Jens Rehsack using code from DBD::File originally written by Jochen
305       Wiedmann and Jeff Zucker.
306
307       The module DBI::DBD::SqlEngine is currently maintained by
308
309       H.Merijn Brand < h.m.brand at xs4all.nl > and Jens Rehsack  < rehsack
310       at googlemail.com >
311
313       Copyright (C) 2010 by H.Merijn Brand & Jens Rehsack
314
315       All rights reserved.
316
317       You may freely distribute and/or modify this module under the terms of
318       either the GNU General Public License (GPL) or the Artistic License, as
319       specified in the Perl README file.
320
321
322
323perl v5.16.3                      2013-04-04     DBI::DBD::SqlEngine::HowTo(3)
Impressum