File Coverage

blib/lib/DBIx/IO.pm
Criterion Covered Total %
statement 9 114 7.8
branch 0 50 0.0
condition 0 20 0.0
subroutine 3 21 14.2
pod 10 13 76.9
total 22 218 10.0


line stmt bran cond sub pod time code
1             #
2             # $Id: IO.pm,v 1.2 2002/05/24 10:31:42 rsandberg Exp $
3             #
4              
5             package DBIx::IO;
6              
7 1     1   5634 use strict;
  1         2  
  1         30  
8              
9 1     1   520 use DBIx::IO::GenLib ();
  1         3  
  1         24  
10              
11 1     1   8 use vars qw($VERSION);
  1         7  
  1         1515  
12              
13             $VERSION = '1.07';
14              
15             =head1 NAME
16              
17             DBIx::IO - Abstraction layer for database I/O with auto-discovery of data dictionary.
18              
19             =head1 INTRODUCTION
20              
21             Why yet another database abstraction layer module (DBAL)?
22             I wrote this before there were any popular abstraction layers on top of DBI available, when DBI itself was just becoming
23             popular. Therefore, I have taken a different approach than the others (Class::DBI and DBIx::Class, etc),
24             providing a set of distinct advantages that are more fitting for some applications.
25             This has been in use for many years at several production
26             sites and I still use it for new projects so hopefully it will be useful to others.
27              
28             Perhaps the most important advantage/distinction DBIx::IO has is auto-discovery of the data dictionary.
29             Compare to Class::DBI and successors where the dictionary information must be duplicated in sub-classes.
30             With auto-discovery there is less code to write/maintain and your DBA can make
31             structural changes that will be immediately recognized. This avoids the hassle of keeping two data sources in sync.
32              
33             See Cruddy! for a quick-start and example implementation:
34              
35             L
36              
37             Other advantages include:
38              
39             =over
40              
41             =item *
42              
43             convenient date format handling and the ability to gracefully handle loose
44             date formats on input (very convenient for user interfaces)
45              
46             =item *
47              
48             driver-specific SQL hints
49              
50             =item *
51              
52             triggers
53              
54             =item *
55              
56             DBIx::IO::Search supports hierarchical queries (START WITH ... CONNECT BY ...)
57              
58             =back
59              
60              
61             Briefly, some advantages of using a DBAL in general:
62              
63             =over
64              
65             =item *
66              
67             Reduce embedded SQL in code
68              
69             =item *
70              
71             Consistent error handling (although using exceptions can be consistent as well)
72              
73             =item *
74              
75             Less mess with datatype mapping to/from the db (especially date handling)
76              
77             =item *
78              
79             Freedom of choice for your RDBMS and ease of migration if needed
80              
81             =item *
82              
83             Code re-use among different RDBMS's. Agreed that facilitating portability is nice but not always practical, one major point Jeremy Zawodny misses in
84             L is that DBAL's allow you to write code for an application
85             that uses one RDBMS and then reuse that code for another RDBMS
86              
87             =back
88              
89             Disadvantages include loss of flexibility for RDBMS-specific features, performance knobs, etc, though this can be somewhat
90             accommodated in thoughtful design of your RDBMS-specific adapter.
91              
92             =head2 Relationships
93              
94             This module has limited ability for defining relationships vs Class::DBI, etc.
95             DBIx::IO::Mask allows simple meta-data relationships to be defined for the convenience of mapping
96             human-readable to machine-efficient indentifiers (lookup tables). Anything more complex requires defining a view for SELECT's or overriding
97             methods to INSERT or UPDATE related records. While the relationship definition features present in DBIx::Class et al can save some coding, they have limitations as well and
98             those authors offer the very same suggestions (using views and overriding methods) for
99             anything complex.
100              
101             =head2 RDBMS Support
102              
103             Adapters for Oracle and MySQL are stable - for others, volunteers are welcome (please contact me).
104              
105             =head2 Best Practices
106              
107             Sound database design is key - starting with a solid yet flexible schema, and leveraging SQL (views, user-defined functions with PL/SQL, etc) can save months of coding and ongoing maintenance.
108             There are a few shortcuts within this library you can take advantage of if you use suggested naming conventions (see DBIx::IO::Table, DBIx::IO::Mask, etc).
109             Briefly, for a given table PARENT_TABLE, a field named PARENT_TABLE.PARENT_TABLE_ID will be assumed the primary key; for
110             a related table CHILD_TABLE, the column CHILD_TABLE.PARENT_TABLE_ID will be assumed a foreign key with a
111             primary key in PARENT_TABLE.PARENT_TABLE_ID. An analagous relationship exists for CHILD_TABLE.PARENT_TABLE and PARENT_TABLE.PARENT_TABLE
112             (same thing without the '_ID' appended). These assumptions can of course be overridden to fit your own best practices.
113              
114             =head2 Next Steps
115              
116             You probably won't ever use this module directly, from here you should probably review DBIx::IO::Table and DBIx::IO::Search. Enjoy!
117              
118              
119             =head1 SYNOPSIS
120              
121             use DBIx::IO;
122              
123             Virtual base class - you won't use this module directly.
124              
125             =head2 Methods
126              
127             $io = new DBIx::IO($dbh,$table_name,[$key_name]);
128              
129              
130             $qualified_value = $io->qualify($value,[$column_name],[$date_format],[$datatype]);
131              
132             $datatype = $io->column_type($field_name);
133              
134             $integer = $io->field_length($field_name);
135              
136             $bool = $io->required($field_name);
137              
138             $default_value = $io->default_value($field_name);
139              
140             $rv = $io->verify_datatype($value,[$field_name],[$type]);
141              
142             $row = $io->fetch($id_val_or_id_hash,[$key_name]);
143              
144             $rv = $io->delete_by_id($id_value,[$key_name]);
145              
146             $rv = $io->delete_all($id_hash);
147              
148             $rv = $io->update_hash($update_hash,$id_val_or_id_hash,[$date_format],[$hint]);
149              
150             $rv = $io->insert_hash($insert_hash,[$date_format]);
151              
152             $sth = $io->make_cursor($query_sql);
153              
154             $next_id_val = $io->next_id([$table_name]);
155              
156             $column_types = $io->column_types();
157              
158             =head2 Attribute Accessors/Modifiers
159              
160             Get the values of these READ-ONLY attributes.
161              
162             $table_name = $io->table_name();
163             $dbh = $io->dbh();
164              
165             $key_name = $io->key_name();
166             May return undef if multi-part key.
167              
168              
169             =head1 DESCRIPTION
170              
171             Methods are provided to perform basic database I/O via DBI without having to embed SQL in your programs. Records are normally passed in and out
172             in the form of hash references where keys of the hash represent columns (ALWAYS UPPER CASE), and the values are the corresponding column values.
173             For inserts, the primary key is usually auto-generated, assuming a few obvious conditions are met (DWIM, see insert()).
174             See DBIx::IO::GenLib for a discussion of the canonical date format, which will be used by default throughout these methods.
175             Bind variables are generally not used so, for performance reasons, you may be better off NOT using these methods if favor of bind variables if high
176             volumes of db IO will occur.
177              
178             Virtual base class - must be subclassed by RDBMS-specific driver module. Please see driver-specific subclasses for details on many methods.
179              
180             =head2 Messages and Logging
181              
182             Warnings are handled similar to DBI, specifically, if the PrintError attribute is set
183             in the db handle, errors/warnings will be displayed (PrintError is set by default).
184              
185             =head1 METHOD DETAILS
186              
187             =over 4
188              
189             =item C (constructor)
190              
191             $io = new DBIx::IO($dbh,$table_name,[$key_name]);
192              
193             Create a new $io object for database I/O operations.
194             A valid DBI (or DBIAccess) database handle must be given.
195             $table_name must be given and its attributes and column names will be discovered
196             and saved with the object.
197             Return undef if unsuccessful or error.
198             Return 0 if $table_name doesn't exist.
199              
200             MySQL users:
201             If your platform has case-sensitive table names (Linux/UNIX), do yourself a favor and set lower_case_table_names=1 in /etc/my.cnf
202             and always use lower case names for tables.
203              
204             =cut
205              
206             ##at memory usage and performance:
207             ##at could save a lot by combining 4 hashes of this object into 1
208             ##at there are 4 hashes that all contain all column names - column_types,defaults,lengths,required
209             ##at more efficient to have 1 hash where each value is a hash with the 4 keys listed above
210             ##at also if I'm using Tie::IxHash I could get rid of the column name array
211             sub new
212             {
213 0     0 1   my ($caller,$dbh,$table_name,$key_name) = @_;
214 0   0       my $class = ref($caller) || $caller;
215            
216 0 0         ref($dbh) || (warn("\$dbh doesn't appear to be valid"), return undef);
217 0           $dbh->{LongReadLen} = $DBIx::IO::GenLib::LONG_READ_LENGTH;
218            
219 0 0         defined($table_name) || (warn("\$table_name not defined"), return undef);
220 0           my $self = bless({},$class);
221 0           $self->{dbh} = $dbh;
222              
223 0           my $rv;
224 0 0         unless ($rv = $self->_assign_table_attrs($table_name,$key_name))
225             {
226 0 0         defined($rv) || warn("Could not get table attributes");
227 0           return $rv;
228             }
229            
230 0           return $self;
231             }
232              
233             sub table_name
234             {
235 0     0 0   my $self = shift;
236 0           return $self->{table_name};
237             }
238              
239             sub dbh
240             {
241 0     0 0   my $self = shift;
242 0           return $self->{dbh};
243             }
244              
245             sub key_name
246             {
247 0     0 0   my $self = shift;
248 0           return $self->{key_name};
249             }
250              
251             =pod
252              
253             =item C
254              
255             $qualified_value = $io->qualify($value,[$column_name],[$date_format],[$datatype]);
256              
257             Qualify $value and make it digestible by the db engine, usually for updates or inserts when bind variables are not involved.
258             $column_name or $datatype must be given. If $column_name is given the column's datatype is
259             taken from the column types discovered in the constructor. Otherwise you must manually
260             specify $datatype.
261             See DBIx::IO::GenLib for a list of supported datatypes and corresponding constants that may be used for $datatype.
262              
263             For character datatypes this method strips null "\0" characters because DBI sees these
264             characters as string terminators (a C standard).
265             If for some reason null chars are desirable, use bind variables.
266              
267             For dates, the canonical date format is assumed (see DBIx::IO::GenLib)
268             unless $date_format is defined. If the date format is unknown or suspect, (e.g. dates entered by humans) assign
269             the constant $UNKNOWN_DATE_FORMAT to $date_format and the format will be discovered via DBIx::IO::GenLib::normalize_date()
270             (extremely convenient at the cost of performance).
271              
272             If $value is undefined, $qualified_value will return as the string 'NULL').
273             Return undef if error.
274              
275             See also insert_hash() and update_hash() for an implementation.
276              
277             For performance considerations, refer to driver-specific docs for driver-specific implemented methods.
278              
279             =cut
280              
281             =pod
282              
283             =item C
284              
285             $rv = $io->verify_datatype($value,[$field_name],[$type]);
286              
287             NOTE: Use DBIx::IO::GenLib::normalize_date to verify dates.
288              
289             Verify the datatype of $value. Mostly useful for numerical
290             values. $field_name or $type must be given.
291              
292             Return 0 if a numeric type was required but not given.
293             Return -1 if a decimal was given and will be rounded to an integer.
294              
295             mysql users:
296             Return -2 if a negative number was given for an unsigned integer type.
297              
298             =cut
299              
300             =pod
301              
302             =item C
303              
304             $default_value = $io->default_value($field_name);
305              
306             Return the default value listed in the data dictionary
307             for $field_name.
308             See also column_types().
309              
310             =cut
311             sub default_value
312             {
313 0     0 1   my ($self,$field) = @_;
314 0           return $self->{defaults}{uc($field)};
315             }
316              
317              
318             =pod
319              
320             =item C
321              
322             $bool = $io->required($field_name);
323              
324             Return true if $field_name is listed as NOT NULL in the data dictionary.
325             See also column_types().
326              
327             =cut
328             sub required
329             {
330 0     0 1   my ($self,$field) = @_;
331 0           return $self->{required}{uc($field)};
332             }
333              
334              
335             =pod
336              
337             =item C
338              
339             $integer = $io->field_length($field_name);
340              
341             Return the maximum length of $field_name according to the data dictionary.
342             Length will be compensated for numbers with decimals, and sign.
343             See also column_types().
344              
345             =cut
346             sub field_length
347             {
348 0     0 1   my ($self,$field) = @_;
349 0           return $self->{lengths}{uc($field)};
350             }
351              
352             # private sub for constructor
353             # return 0 if no columns for $table could be found
354             # Return undef if an invalid key_name was passed in
355             # semi-virtual method (yay perl!) must be overridden to set attribute from data dictionary
356             ##at should do away with the whole concept of $key_name and use $keys or equiv
357             sub _assign_table_attrs
358             {
359 0     0     my ($self,$table_name,$key_name) = @_;
360 0           my $ct;
361 0           $self->{pk} = [];
362 0 0         unless ($ct = $self->column_attrs($table_name))
363             {
364 0           return $ct;
365             }
366 0           my $kn;
367 0 0         if (($kn = uc($key_name)))
    0          
368 0           {
369 0 0         exists($ct->{$kn}) || (warn("Key: $kn does not exist as a column in $table_name"),return undef);
370             }
371             elsif (@{$self->{pk}} == 1)
372             {
373 0           $kn = $self->{pk}[0];
374             }
375             else
376             {
377 0           undef($kn);
378             }
379 0           $self->{key_name} = $kn;
380 0           $self->{table_name} = $table_name;
381 0           return 1;
382             }
383              
384              
385             =pod
386              
387             =item C
388              
389             $column_types = $io->column_types();
390              
391             Get the column names and associated data types for $table_name (can be given to the constructor).
392             The return value is a hash ref of column => datatype pairs.
393             By convention, column names are in UPPER CASE.
394             The column types are returned in UPPER CASE (not by convention, but
395             compatible with the data types defined for use with qualify())
396              
397             The attributes are cached for each table requested for any object of this class
398             so the database may not be queried each time this method is called.
399              
400             Oracle users:
401             If $table_name is a concrete table (rather than a view, for instance)
402             ROWID will be included as a column with ROWID datatype. You may find this
403             useful for updates and deletes (See also DBIx::IO::GenLib for a ROWID column name constant).
404              
405             =cut
406             sub column_types
407             {
408 0     0 1   my ($self) = @_;
409 0 0         ref($self) || (warn("\$self not an object"),return undef);
410 0           return $self->{column_types};
411             }
412              
413             =pod
414              
415             =item C
416              
417             $datatype = $io->column_type($field_name);
418              
419             Return the datatype of $field_name
420             See also column_types().
421              
422             =cut
423             sub column_type
424             {
425 0     0 1   my ($self,$field) = @_;
426 0           return $self->{column_types}{uc($field)};
427             }
428              
429             =pod
430              
431             =item C
432              
433             $sth = $io->make_cursor($query_sql);
434              
435             Prepare and execute $query_sql and return the statement handle ($sth).
436             Error checking is done at each step. (This is useless however, if
437             the RaiseError db attribute is true)
438             Returns undef if error.
439              
440             =cut
441             sub make_cursor
442             {
443 0     0 1   my ($self,$sql) = @_;
444 0   0       my $sth = $self->{dbh}->prepare($sql) || return undef;
445 0 0         $sth->execute() || return undef;
446 0           return $sth;
447             }
448              
449             =pod
450              
451             =item C
452              
453             $rv = $io->insert_hash($insert_hash,[$date_format]);
454              
455             Insert a row with name value pairs contained
456             in $insert_hash. Values will be automatically qualified
457             according to column datatypes so don't pre-qualify them.
458             For date values, the canonical format is assumed
459             (see qualify()) unless $date_format is specified.
460              
461             This method is useful because it automagically
462             qualifies each insert value using qualify().
463             Also, if the table has an integral primary key,
464             and the corresponding key in $insert_hash was not given, a value
465             for will be generated.
466              
467             MySQL users:
468             This assumes the primary key was declared with AUTO_INCREMENT, so no extra work is done
469             except to pass the newly generated value back in $rv.
470              
471             Oracle users:
472             The situation described above assumes an Oracle sequence object named
473             SEQ_$table_name has been created. This is the conventional naming scheme so that this feature
474             can be taken advantage of in most cases. E.g., if inserting into table MEMBER, an associated
475             SEQUENCE object named SEQ_MEMBER must also exist. See C.
476              
477             In short, you generally don't have
478             to supply a table's primary key if that primary key is a sequenced ID column.
479              
480             Return the generated pk ID value or -1.2 if there wasn't a value generated (e.g. if the table has a multi-column pk)
481             If there was no data to insert, -1.1 is returned.
482             Return undef if error.
483              
484              
485             =cut
486              
487             =pod
488              
489             =item C
490              
491             $row = $io->fetch($id_val_or_id_hash,[$key_name]);
492              
493             Return a row in hashref form (COLUMN_NAME => value pairs).
494             All date values are returned in the canonical format (see DBIx::IO::GenLib).
495              
496             The row to be fetched is identified depending on the datatype of $id_val_or_id_hash.
497              
498             If $id_val_or_id_hash is a scalar, the value is used in conjunction with $key_name.
499             $key_name defaults to the table's primary key.
500             If $id_val_or_id_hash is a hash ref it is interpreted as column => value
501             pairs to be AND'ed together in a WHERE clause.
502              
503             This method assumes that key(s) given form a unique key, so only 1 row is returned.
504              
505             Oracle users:
506             LOB columns won't be retreived because they aren't supported in DBD::Oracle (as of v1.19). LONG columns seem to work
507             fine though so if you can get away with using a LONG over a LOB, do that.
508             $DBIx::IO::GenLib::LONG_READ_LENGTH gives the limit size of a long that will be returned.
509             If the table is a concrete table (rather than a view, for instance)
510             ROWID will be included as a column with ROWID datatype. You may find this
511             useful for updates and deletes (See also DBIx::IO::GenLib for a ROWID column name constant).
512              
513              
514             Return undef if error.
515             Return 0 if no row was found.
516              
517             =cut
518             sub fetch
519             {
520 0     0 1   my ($self,$key,$key_name) = @_;
521 0 0         ref($self) || (warn("\$self not an object"),return undef);
522 0           my $table = $self->table_name();
523              
524 0 0         unless (ref($key))
525             {
526 0   0       $key_name = uc($key_name) || $self->key_name() || ($self->_alert("No key column name given"),return undef);
527 0 0         exists($self->{column_types}->{$key_name}) || ($self->_alert("$key_name is not a column of $table, does $table have a multi-part key?"), return undef);
528 0           $key = { $key_name => $key };
529             }
530              
531 0   0       my $where = $self->_build_where_clause($key) || return undef;
532 0           my $cols = $self->{select_cols};
533 0   0       my $sth = $self->make_cursor("SELECT $cols FROM $table $where") || return undef;
534 0           my $rv = $sth->fetchrow_hashref();
535 0 0         $sth->err && ($self->_alert("Error fetching from $table $where"), return undef);
536             # Safeguard so that we know %$rv evaluation won't cause a runtime error
537 0 0         ref($rv) || return 0;
538 0 0         return (%$rv ? $rv : 0);
539             }
540              
541             =pod
542              
543             =item C
544              
545             $rv = $io->delete_by_id($id_value,[$key_name]);
546              
547             Delete a row where $key_name = $id_value.
548             $key_name defaults to the primary key.
549              
550             Returns the number of rows deleted or false if error (0 is represented as '0E0' which is true).
551             A maximum of 1 row can be deleted here, it is up to you to make sure that the given
552             key is unique, otherwise unexpected results can occur. See also delete_all().
553              
554             =cut
555             sub delete_by_id
556             {
557 0     0 1   my ($self,$id_val,$key_name) = @_;
558 0 0         ref($self) || (warn("\$self not an object"),return undef);
559 0           my $table = $self->table_name();
560 0   0       $key_name = uc($key_name) || $self->key_name() || ($self->_alert("No key column name given"),return undef);
561 0 0         exists($self->{column_types}->{$key_name}) || ($self->_alert("$key_name is not a column of $table, does $table have a multi-part key?"), return undef);
562 0           $id_val = $self->qualify($id_val,$key_name);
563 0 0         unless (defined($id_val))
564             {
565 0           $self->_alert("Unable to qualify ID value: qualify($id_val,$key_name)");
566 0           return undef;
567             }
568 0           my $sql = "DELETE FROM $table WHERE $key_name = $id_val";
569              
570             # limit the number of rows deleted.
571 0           $self->limit($sql,1,'AND');
572 0           my $dbh = $self->dbh();
573 0           return $dbh->do($sql);
574             }
575              
576              
577             =pod
578              
579             =item C
580              
581             $rv = delete_all($id_hash);
582              
583             Delete all rows that satisfy $id_hash, where $id_hash
584             is a hash of COLUMN => value pairs that will be AND'ed together for the
585             WHERE clause of the DELETE statement.
586              
587             Returns the number of rows affected or false if error (0 is represented as '0E0' which is true).
588             Return -1 if $id_hash is empty or not a reference.
589              
590             =cut
591             sub delete_all
592             {
593 0     0 1   my ($self,$id_hash) = @_;
594 0 0         ref($self) || (warn("\$self not an object"),return undef);
595 0 0 0       (ref($id_hash) && %$id_hash) || return -1;
596              
597 0   0       my $where = $self->_build_where_clause($id_hash) || return undef;
598 0           my $dbh = $self->dbh();
599 0           my $table = $self->table_name();
600 0           return $dbh->do("DELETE FROM $table $where");
601             }
602              
603             =pod
604              
605             =item C
606              
607             $rv = $io->update_hash($update_hash,$id_val_or_id_hash,[$date_format],[$hint]);
608              
609             Update a row with name value pairs contained
610             in $update_hash, a hashref of COLUMN_NAME => new_value pairs.
611             Values will be automatically qualified
612             according to column datatypes so don't pre-qualify them.
613             For date values, the canonical format is assumed
614             unless $date_format is specified (see qualify()).
615              
616             The row(s) to be updated are identified depending on the datatype of $id_val_or_id_hash.
617              
618             If $id_val_or_id_hash is a scalar, the value is used as the primary key.
619             If $id_val_or_id_hash is a hash ref it is interpreted as COLUMN_NAME => value
620             pairs to be AND'ed together in a WHERE clause.
621              
622             This method supports driver-specific SQL hints contained in $hint.
623              
624             Return the number of rows affected or false if error (0 is represented as '0E0' which is true).
625             Return -1 if there was no data to update.
626              
627             =cut
628              
629             # warn if PrintError (from $dbh) flag is on.
630             sub _alert
631             {
632 0     0     my ($self,$message) = @_;
633 0 0         warn($message) if $self->{dbh}->{PrintError};
634             }
635              
636             # return the argument with "_ID" appended
637             # argument is assumed to be a table_name and the return
638             # value is assumed to be the name of the table's pk.
639             sub _id_name
640             {
641 0     0     my ($caller,$table) = @_;
642 0           ($table) = $caller->_strip_owner($table);
643 0           return uc($table) . "_ID";
644             }
645              
646             sub _strip_owner
647             {
648 0     0     my ($caller,$object) = @_;
649 0 0         if ($object =~ /(.*)\.(.*)/)
650             {
651 0           return ($2,$1);
652             }
653 0           return ($object);
654             }
655              
656             sub _build_where_clause
657             {
658 0     0     my ($self,$keys) = @_;
659 0 0         ref($keys) || ($self->_alert("\$keys not a hashref"), return undef);
660 0           my ($col,$val);
661 0           my $where = "WHERE ";
662 0           while (($col,$val) = each %$keys)
663             {
664 0           $val = $self->qualify($val,$col);
665 0 0         unless (defined($val))
666             {
667 0           $self->_alert("Unable to qualify ID value: qualify($val,$col)");
668 0           return undef;
669             }
670 0           $where .= "$col = $val AND ";
671             }
672 0           chop $where;
673 0           chop $where;
674 0           chop $where;
675 0           chop $where;
676              
677 0           return $where;
678             }
679              
680             =pod
681              
682             =back
683              
684             =cut
685              
686             1;
687              
688             __END__