• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

ChangesH A D05-Feb-2004696 3118

MANIFESTH A D05-May-200270 77

Makefile.PLH A D09-May-20021.1 KiB4224

READMEH A D27-Jul-200220.5 KiB508380

TableHash.pmH A D05-Feb-200443.6 KiB1,426504

fix_html.perlH A D05-May-2002693 275

test.plH A D22-Mar-2002660 215

README

1NAME
2    DBIx::TableHash - Tie a hash to a mysql table + SQL utils
3
4SYNOPSIS
5        use DBIx::TableHash;
6        my $DBHash = DBIx::TableHash->create_or_die
7        (my $Params =
8         {
9             DBIDriver      => 'mysql',
10             Database       => 'mydatabase',
11             HostName       => 'localhost',
12             Port           => undef,
13             Login          => '',
14             Password       => '',
15
16             TableName      => 'SalesPeople',
17             KeyField       => 'FullName',
18
19             ## For multi-key lookup:
20             FixedKeys      => {AreaCode   => 415,
21                                StatusCode => 'Active',
22                                RecordType => 'Primary'},
23
24             ## To retrieve a single value:
25             ValueField     => 'PhoneNumber',
26
27             ## ... or for "multi-value" retrieval...
28             ValueField     => undef,
29
30             ## ... optionally specifying...
31             RetrieveFields => [qw(Title Territory Quota)],
32
33             ## For caching:
34             CacheMode => 'CacheBeforeIterate'
35             ## or...
36             CacheMode => 'CacheOneTime'
37             ## or...
38             CacheMode => 'CacheNone'
39            }
40         );
41
42        my %DBHash; tie(%DBHash, 'DBIx::TableHash', $Params);
43
44        my $DBHash = DBIx::TableHash->create($Params) or die "Help!";
45        my $DBHash = DBIx::TableHash->create_or_die($Params);
46
47        my $DBHash = DBIx::TableHash->create_copy($Params) or die "Help!";
48        my $DBHash = DBIx::TableHash->create_copy_or_die($Params);
49
50OVERVIEW
51    All parameters are passed via a single anonymous hash.
52
53    All parameters are optional, but you'll almost always need to specify
54    Database, TableName, and KeyField.
55
56    Omitting ValueField puts the hash in "multi-value" mode, where you
57    store/retrieve a hash of fields/values instead of a single value. In
58    "multi-value" mode all fields in each record are retrieved on every
59    fetch; RetrieveFields limits fields retrieved to a specified list.
60
61    Specifying FixedKeys puts the hash in "multi-key" mode, in which only a
62    subset of the database table, corresopnding to records that match the
63    spec in FixedKeys, is operated on.
64
65    Cache modes reduce querying, but lose synchronization and hog memory.
66
67    The object is designed to be easy subclass. Try making a subclass that
68    sets defaults for all or most of the parameters, so the caller doesn't
69    have to supply any at instantiation time.
70
71    "create_copy" methods efficiently create and return potentially huge
72    untied hash "snapshot" of the same data that would have been retrieved
73    by the corresponding tied hash.
74
75DETAILS
76    The DBHash object is designed to tie a hash to a table or a subset of
77    records in a table in a DBI database (only tested with mysql in the
78    current version, but expected to work with any vendor).
79
80    If the table only has a single KeyField, which this modules assumes to
81    be a unique key field in the table, then the hash keys are stored and
82    retrieved in that field, and the values are saved in and returned from
83    the ValueField. Records are automatically created and deleted as the
84    hash is used like any other hash. (If the table is read-only, be sure
85    not to try to store into the tied hash!)
86
87    To access only a subset of the records in a table, you may specify hash
88    of "FixedKeys", which is a hash mapping OTHER field names to fixed
89    values that those fields must have for all lookups, updates, and inserts
90    done via the hash interface. This sets up a virtual hash corresponding
91    to a subset of the table where N key fields are fixed at given values
92    and a different key field may vary.
93
94    There are several ways to use this module.
95
96    Quick and dirty mode (single-key, single-value) using tie:
97
98        use DBIx::TableHash;
99        my %PhoneNumbers;
100        tie (%PhoneNumbers, 'DBIx::TableHash',
101             {Database      => 'mydatabase',
102              TableName     => 'SalesPeople',
103              KeyField      => 'FullName',
104              ValueField    => 'PhoneNumber'})
105            or die "Failed to connect to database";
106
107    Then you can use %PhoneNumbers like any hash mapping FullName to
108    PhoneNumber. Any retrieval of data results in corresponding SQL queries
109    being made to the database. Modifying the hash modifies the database.
110
111    Even quicker mode using create():
112
113    For convenience, you can use the create() class method to do the tying
114    for you. It creates an anonymous hash, ties it and, returns it. It takes
115    the same parameters as new() and tie().
116
117        use DBIx::TableHash;
118        my $PhoneNumbers = DBIx::TableHash->create(......)
119            or die "Failed to connect to database";
120
121    Quicker still using create_or_die():
122
123        use DBIx::TableHash;
124        my $PhoneNumbers = DBIx::TableHash->create_or_die(......);
125
126    create() carps and returns undef if it can't connect to the database.
127
128    create_or_die() croaks (dies) your program, with the same error message
129    as create() would have given.
130
131    Normally, create() will carp() (warn) you with an error message upon
132    failure, and return undef.
133
134    If you would have handled your error by saying "or die", and ar
135    comfortable with create's error message rather than your own, then
136    create_or_die() is for you.
137
138    Using one of the create() methods instead of new() and tie() works with
139    all of the different modes discussed below, and all parameters are the
140    same either way.
141
142    Cooler subclassing mode:
143
144    You can create a simple subclass that provides default parmas in an
145    initialize method so they don't have to be provided by the caller ...
146
147        ### MyCompany/SalesPhoneHash.pm:
148
149        #!/usr/bin/perl
150
151        use strict;
152
153        package   MyCompany::SalesPhoneHash;
154        use       vars qw(@ISA);
155        use       DBIx::TableHash;
156        @ISA = qw(DBIx::TableHash);
157
158        sub initialize
159        {
160            my $this = shift;
161
162            $this->{Database}   ||= 'mydatabase';   ## Name of database to connect to.
163            $this->{TableName}  ||= 'SalesPeople';  ## Table in which to store the data
164            $this->{KeyField}   ||= 'FullName';     ## Name of the key field
165            $this->{ValueField} ||= 'PhoneNumber';  ## Name of the value field.
166
167          done:
168            return($this->SUPER::initialize());
169        }
170        1;
171
172    Then to use the object, your script merely does:
173
174        use MyCompany::SalesPhoneHash;
175        my %PhoneNumbers = MyCompany::SalesPhoneHash->create_or_die();
176
177    Of course, when instantiating a subclass, if you wish you can still
178    override any parameters you wish, as long as the initialize() method in
179    the subclass uses ||= rather than = to set defaults for any unspecified
180    parameters.
181
182    Multi-key mode:
183
184    You may also use the "FixedKeys" parameter to specify a hash of some
185    additional key fields and their fixed values that must match exactly for
186    any records that are retrieved, deleted, or created by the tied object,
187    effectively allowing the hash to operate on only a subset of the data in
188    the database. This is typically helpful in a multi-keyed table where,
189    for the purposes of your script, all key values should be fixed except
190    one (and that one is the hash key).
191
192        use DBIx::TableHash;
193        my $PhoneNumbers =
194            DBIx::TableHash->
195                create_or_die(
196                              {Database     => 'mydatabase',
197                               TableName    => 'SalesPeople',
198                               KeyField     => 'FullName',
199                               ValueField   => 'PhoneNumbers',
200                               FixedKeys        =>
201                               {AreaCode        => 415,
202                                StatusCode      => 'Active',
203                                RecordType      => 'Primary'}});
204
205    Multi-value mode:
206
207    If instead of getting and setting a single value, you'd like to get or
208    set a hash of all fields in the record, simply don't specify ValueField,
209    and the object will use "multi-value" mode, where an entire record, as a
210    hash, is gotten or set on each fetch or store. Feel free to combine this
211    mode with multi-key mode.
212
213    When storing a record in multi-value mode, if the record already exists,
214    only the specified fields are overwritten. If it did not already exist,
215    then only the specified fields will be written and the others will be
216    NULL or defaulted according to the table schema.
217
218    When storing a record in multi-value mode, you can't change the values
219    of the primary key field or any other key field specified in FixedKeys
220    (if any), since that would mess up the whole point of this module which
221    is to leave the main key and fixed keys fixed while mucking with the
222    other values in the record. Any changed values in key fields are simply
223    ignored.
224
225        use DBIx::TableHash;
226        my $SalesPeopleTable =
227            DBIx::TableHash->
228                create_or_die(
229                              {Database     => 'mydatabase',
230                               TableName    => 'SalesPeople',
231                               KeyField     => 'FullName'});
232
233        my $SalesPersonFullName = "Joe Jones";
234        my $EntireRecord = $SalesPeopleTable->{$SalesPersonFullName};
235
236    When fetching records in multi-value mode, you can limit the list of
237    returned fields to a subset of all available fields in case there might
238    be some very big ones that you don't want to waste bandwidth getting.
239    Just set the RetrieveFields parameter to an anonymous list of the fields
240    you care to retrieve. (This setting does not limit the fields you can
241    SET, just the ones that get retrieved.)
242
243        use DBIx::TableHash;
244        my $SalesPeopleTable =
245            DBIx::TableHash->
246                create_or_die(
247                              {Database     => 'mydatabase',
248                               TableName    => 'SalesPeople',
249                               KeyField     => 'FullName',
250                               RetrieveFields=> [qw(Territory Quota)]});
251
252    Warning:
253
254    In multi-value mode, you might expect that this:
255
256        $Hash->{$MyKey}->{FieldX} = 'foo';
257
258    would set the value of the FieldX field in the appropriate record in the
259    database. IT DOES NOT.
260
261    This is because the anonymous hash returned by $Hash->{$MyKey} is not in
262    any way tied back to the database. You'd have to retrieve the record
263    hash, change any value in it, and then set $Hash->{$MyKey} back to it.
264
265    Making the above syntax work with a multi-valued tied hash to set a
266    value in the database is a possible future enhancement under
267    consideration by the author. Let me know if you would like to have that
268    work.
269
270    In the meanwhile, here's how you do could do it:
271
272        (my $Record = $Hash->{$MyKey})->{FieldX} = 'foo';
273        $Hash->{$MyKey}->{FieldX} = $Record;
274
275    WARNING: If you use the above approach to update a record in multi-value
276    mode, beware that there's potentially a race condition in the above code
277    if someone else updates the same record after you've copied it but
278    before you've modified and set it. So use this technique with caution
279    and understanding. If in doubt, don't use this module and instead use an
280    SQL query to update the record in a single transaction. Only you know
281    the usage patterns of your database, the concurrency issues, and the
282    criticality of errors.
283
284    Caching modes:
285
286    The object has several ways it can cache data to help minimize the
287    number of SQL queries to the database, at the expense of potentially
288    dramatically increased memory usage. The following cache parameters can
289    be specified to enable caching:
290
291        CacheMode => 'CacheBeforeIterate'
292
293        CacheMode => 'CacheOneTime'
294
295    To disable caching, specify:
296
297        CacheMode => 'CacheNone'
298
299    (You can also assign undef to CacheMode, but you'll get warnings.)
300
301    Normally, every time you fetch a value from the hash, it makes an SQL
302    query to the database. This, of course, is the intended and normal mode
303    of operation.
304
305    Unfortunately, in Perl, just calling values(%Hash) or each(%Hash) or
306    even copying the hash with {%Hash} results in a separate FETCH, and
307    consequently, a separate SQL query made by this module, for each item.
308    This could result in thousands of queries just to fetch all the values
309    from a thousand-item table in the database.
310
311    However, often you want to iterate over all the elements of a hash
312    without it having to go back to the database and issue another query for
313    each item that you retrieve.
314
315    Using the 'CacheBeforeIterate' mode, all keys and values are cached upon
316    each call to FIRSTKEYS (i.e. at the start of any iteration or
317    enumeration). Then, any subsequent calls to FETCH data from the hash
318    retrieve it from the cache instead of doing an SQL query. STORING or
319    DELETING any items from the hash results in them being stored and
320    deleted from both the database and the cache.
321
322    Using the CacheOneTime mode, the full cache is built at object
323    instantiation and time never fully rebuilt. In fact, its contents never
324    change unless you make alterations by using it to store into and/or
325    delete from the database.
326
327    CACHE WARNING: With both caching modes, of course, you must be
328    comfortable with the fact that the data being retrieved is a "snapshot"
329    of the database and consequently will not reflect updates done by other
330    parties during the lifetime of the object; it will only reflect updates
331    that you make by storing or deleting values from it. If other people are
332    using the database simultaneously, your cache and the actual data could
333    "drift" out of agreement. This is mainly dangerous to you, not others,
334    unless you then go make updates to the data based on potentially
335    outdated values.
336
337    All modes may be combined...
338
339    All modes and parameters are orthogonal, so any combination of
340    parameters may be specified, with the exception that the RetrieveFields
341    parameter is only meaningful when ValueField is not unspecified.
342
343    With subclassing, you may create objects that pre-specify any
344    parameters, even those that affect the major modes of operation. For
345    example, you may combine the subclassing technique and the multi-key
346    mode to make an object that accesses only the appropriate subset of a
347    multi-keyed table without requiring any parameters to be supplied by the
348    caller.
349
350    Getting a COPY of the data instead of a tied hash:
351
352    What if you just want a big copy -- a snapshot -- of the data in the
353    table, in a regular old hash that's no longer tied to the database at
354    all? (Memory constraints be damned!)
355
356    Just use the create_copy() or create_copy_or_die() methods. They work
357    just like create() and create_or_die(), but instead of returning a tied
358    object, they just return a potentially huge hash containing a copy of
359    all the data.
360
361    In other words:
362
363       create_copy()        is equivalent to: {%{create() || {} }}
364       create_copy_or_die() is equivalent to: {%{create_or_die()}}
365
366    ... but the _copy methods are more efficient because internally, a
367    caching mode is used to minimize the queries to the database and
368    generate the hash as efficiently as possible.
369
370    In all other respects, create_copy() and create_copy_or_die() perform
371    exactly like their non-copying namesakes, taking all the same
372    parameters, except CacheMode which is not relevant when making a static
373    copy.
374
375    Please remember that the object returned by the _copy methods is no
376    longer tied to the database.
377
378PARAMETER SUMMARY
379    The full list of recognized parameters is:
380
381    DBI Parameters
382
383        Param       Default         Description
384        ------------------------------------------------------------------------
385        DBIDriver   'mysql'         Name of DBI driver to try to use (only
386                                        mysql has currently been tested by the
387                                        author).
388
389        HostName    'localhost'     Host name containing the database and table;
390
391        Port        undef           Port number if different from the standard.
392
393        Login       ''              Login to use when connecting, if any.
394
395        Password       ''               Password to use when connecting, if any.
396
397    SQL Parameters
398
399        Param       Default         Description
400        ------------------------------------------------------------------------
401        Database    ''              Name of database to connect to.
402
403        TableName   ''              Table to connect to.
404
405        KeyField    ''              Name of field in which lookup key is found.
406
407        ValueField  ''              Name of field to pull value from.
408                                    If empty or undef, then a
409                                    multi-value hash is used both for
410                                    saving and retrieving.  This is
411                                    called "multi-value mode".
412
413    Module Parameters
414
415        Param       Default         Description
416        ------------------------------------------------------------------------
417        FixedKeys   {}              If supplied, gives names and
418                                    fixed, hardcoded values that other
419                                    keys in the table must have; this
420                                    effectively limits the scope of
421                                    the tied hash from operating over
422                                    the entire table to operating over
423                                    just the subset of records that
424                                    match the values in FixedKeys.
425                                    This is called "multi-key mode".
426
427        RetrieveFields  []          In multi-value mode, limits the
428                                    fields that are retrieved; default
429                                    is all fields in the record.
430
431SUPPORT
432    I am unable to provide any technical support for this module. The whole
433    reason I had to make it was that I was way too busy (lazy?) to write all
434    that SQL code...
435
436    But you are encouraged to send patches, bug warnings, updates, thanks,
437    or suggestions for improvements to the author as listed below.
438
439    Just be aware that I may not have time to respond. Please be sure to put
440    the name of this module somewhere in the Subject line.
441
442    The code is a pretty simple tied hash implementation, so you're on your
443    own to debug it. If you're having trouble debugging via the "tie"
444    interface, try instantiating an object directly (or retrieving it when
445    you tie (see perltie)) and calling its methods individually. Use the
446    debugger or Data::Dumper to dump intermediate values at key points, or
447    whatever it takes. Use your database server logs if you want to see what
448    SQL code is getting generated. Or contribute a debugging mode to this
449    module which prints out or logs the SQL statements before executing
450    them.
451
452BUGS/GOTCHAS
453    Problem: If you iterate or enumerate the hash, all keys get pulled in
454    from the database and stay stored in memory for the lifetime of the
455    object. FIRSTKEY, which is called every time you do a keys(), each() or
456    any full iteration or enumeration over the tied hash (such as copying
457    it) retrieves and hangs on to a full list of all keys in KeyField. If
458    the keys are long or there are lots of them, this could be a memory
459    problem. (Don't confuse this with CacheMode in which BOTH keys AND
460    values are stored in memory.)
461
462    Solutions:
463
464        1) Don't iterate or enumerate.  Just fetch and store.
465        2) Only iterate or enumerate on short tables.
466        3) LValue or RValue hash slices should be safe to do.
467
468INSTALLATION
469    Using CPAN module:
470
471        perl -MCPAN -e 'install DBIx::TableHash'
472
473    Or manually:
474
475        tar xzvf DBIx-TableHash*gz
476        cd DBIx-TableHash-?.??
477        perl Makefile.PL
478        make
479        make test
480        make install
481
482SEE ALSO
483    The DBIx::TableHash home page:
484
485        http://christhorman.com/projects/perl/DBIx-TableHash/
486
487    The implementation in TableHash.pm.
488
489    The perlref and perltie manual pages.
490
491    The mysql home page:
492
493        http://mysql.com/
494
495THANKS
496    Thanks to Mark Leighton Fisher <fisherm@tce.com> for providing a patch
497    to fix -w support (change in standard "none" setting of CacheMode from
498    undef to CacheNone).
499
500AUTHOR
501    Chris Thorman <chthorman@cpan.org>
502
503    Copyright (c) 1995-2002 Chris Thorman. All rights reserved.
504
505    This program is free software; you can redistribute it and/or modify it
506    under the same terms as Perl itself.
507
508