File Coverage

blib/lib/BW/DB/CRUD.pm
Criterion Covered Total %
statement 15 162 9.2
branch 0 146 0.0
condition 0 28 0.0
subroutine 5 25 20.0
pod 0 20 0.0
total 20 381 5.2


line stmt bran cond sub pod time code
1             # CRUD.pm
2             # by Bill Weinman -- Database CRUD
3             # Copyright (c) 1995-2008 The BearHeart Group, LLC
4             #
5             # based upon the old bwDB.pm
6             # Note bene: CRUD is Create, Retrieve, Update, and Delete
7             #
8             # See POD for History
9             #
10             package BW::DB::CRUD;
11 1     1   899 use strict;
  1         3  
  1         41  
12 1     1   5 use warnings;
  1         3  
  1         31  
13              
14 1     1   6 use Digest::MD5;
  1         4  
  1         38  
15 1     1   13 use BW::Constants;
  1         2  
  1         69  
16 1     1   6 use base qw( BW::DB BW::Base );
  1         1  
  1         2052  
17              
18             our $VERSION = "0.10";
19              
20             # _setter_getter entry points
21 0     0 0   sub id_type { BW::Base::_setter_getter(@_); }
22              
23             ### main crud methods
24              
25             # sub getrec( table, id, options ... )
26             #
27             # caching getrec
28             #
29             # options:
30             # refresh -- force refresh of cache from database
31             #
32             sub getrec
33             {
34 0     0 0   my $sn = 'getrec';
35 0 0         my $self = shift or return undef;
36 0 0         my $t = shift or return $self->_error("$sn: no table name");
37 0 0         my $id = shift or return $self->_error("$sn: no id");
38 0   0       my $opt = shift || '';
39 0           my $rc;
40              
41 0           my $cache = $self->{db_cache}{$t}{$id};
42 0 0 0       if ( $cache and @$cache and $opt ne 'refresh' ) {
      0        
43 0           return $cache;
44             } else {
45 0           $rc = $self->get( $t, "${t}_id", $id );
46 0 0 0       return $self->{db_cache}{$t}{$id} = $rc if $rc and @$rc;
47             }
48              
49 0           return VOID;
50             }
51              
52             # sub putrec( table, id, rechash )
53             #
54             # caching update and insert
55             #
56             # old records will be updated based on id or rechash->{table_id}
57             # new records will be inserted with id or a new id
58             #
59             sub putrec
60             {
61 0     0 0   my $sn = 'putrec';
62 0 0         my $self = shift or return undef;
63 0 0         my $t = shift or return $self->_error("$sn: no table name");
64 0   0       my $id = shift || ''; # can be blank for new recs
65 0 0         my $rechash = shift or return $self->_error("$sn no rechash");
66 0           my $table_id = $t . '_id';
67              
68 0 0 0       if ( $rechash->{$table_id} and !$id ) {
69 0           $id = $rechash->{$table_id};
70             }
71              
72 0 0 0       if ( $id and $self->getrec( $t, $id ) ) {
73 0 0         $self->update( $t, $table_id, $id, $rechash ) or return FAILURE;
74             } else {
75 0 0         $id = $rechash->{$table_id} = $self->gen_id unless $id;
76 0 0         $self->insert( $t, $rechash ) or return FAILURE;
77             }
78              
79 0           return $self->{db_cache}{$t}{$id} = $rechash;
80             }
81              
82             # sub delrec( table, id )
83             #
84             # caching delete
85             #
86             sub delrec
87             {
88 0     0 0   my $sn = 'delrec';
89 0 0         my $self = shift or return undef;
90 0 0         my $t = shift or return $self->_error("$sn: no table name");
91 0   0       my $id = shift || ''; # can be blank for new recs
92              
93             # delete from cache
94 0 0         delete $self->{db_cache}{$t}{$id} if $self->{db_cache}{$t}{$id};
95              
96             # delete from db
97 0           return $self->delete( $t, $t . '_id', $id );
98             }
99              
100             # delete ( table, key, value )
101             # returns TRUE or FALSE
102             sub delete
103             {
104 0 0   0 0   my $self = shift or return undef;
105 0 0         my $table = shift or return undef;
106 0 0         my $key = shift or return undef;
107 0 0         my $value = shift or return undef;
108              
109 0 0         $self->sql_connect unless $self->{dbh};
110 0 0         return $self->_error("Database not connected.") unless $self->{dbh};
111              
112 0           my $query = "DELETE FROM $table WHERE $key = ?";
113 0           return $self->sql_do( $query, $value );
114             }
115              
116             # get ( table, key, value )
117             # returns hashref (from DBI) or FALSE
118             sub get
119             {
120 0     0 0   my $sn = 'get';
121 0           my ( $self, $table, $key, $value ) = @_;
122              
123 0 0         $self->sql_connect unless $self->{dbh};
124 0 0         return $self->_error("Database not connected.") unless $self->{dbh};
125              
126 0           my $query = "SELECT * FROM $table WHERE $key = ?";
127 0           return $self->sql_select( $query, $value );
128             }
129              
130             # search
131             # alias for search_multi -- legacy support for a while
132 0     0 0   sub search { return search_multi(@_) }
133              
134             # search_multi ( table, key, value )
135             # returns arrayref of hashes
136             # use for non-unique keys
137             sub search_multi
138             {
139 0 0   0 0   my $self = shift or return undef;
140 0 0         my $table = shift or return undef;
141 0 0         my $key = shift or return undef;
142 0 0         my $value = shift or return undef;
143              
144 0 0         $self->sql_connect unless $self->{dbh};
145 0 0         return $self->_error("Database not connected.") unless $self->{dbh};
146              
147 0           my $query = "SELECT * FROM $table WHERE $key LIKE ? ORDER BY $key";
148 0           return $self->sql_select( $query, $value );
149             }
150              
151             # update ( table, key, value, { name => value, ... } )
152             sub update
153             {
154 0     0 0   my $sn = 'update';
155 0 0         my $self = shift or return undef;
156 0 0         my $table = shift or return undef;
157 0 0         my $key = shift or return undef;
158 0 0         my $value = shift or return undef;
159 0 0         my $nvpairs = shift or return undef;
160 0           my @cols;
161             my @vals;
162              
163 0 0         $self->sql_connect unless $self->{dbh};
164 0 0         return $self->_error("Database not connected.") unless $self->{dbh};
165              
166 0           foreach my $k ( keys %{$nvpairs} ) {
  0            
167 0           push @cols, "$k = ?";
168 0           push @vals, $nvpairs->{$k};
169             }
170 0 0 0       return undef unless ( @cols and @vals );
171              
172 0           my $query = "UPDATE $table SET " . join( ', ', @cols ) . " WHERE $key = ?";
173              
174 0           push @vals, $value;
175 0           return $self->sql_do( $query, @vals );
176             }
177              
178             ### nv routines
179              
180             # setnv ( object_type, object_id, name, value, attribute, flags, seq )
181             # requires a table named "nv"
182             sub setnv
183             {
184 0 0   0 0   my $self = shift or return undef;
185 0           my $sn = 'setnv';
186 0           my ( $t, $id, $n, $v, $a, $f, $s ) = @_;
187 0 0         return $self->_error("$sn: missing object type") unless $t;
188 0 0         return $self->_error("$sn: missing object id") unless $id;
189 0 0         return $self->_error("$sn: missing name") unless $n;
190 0 0         return $self->_error("$sn: missing value") unless $v;
191              
192 0           my $rec = {
193             object_type => $t,
194             object_id => $id,
195             name => $n,
196             value => $v
197             };
198              
199 0 0         $rec->{attribute} = $a if $a;
200 0 0         $rec->{flags} = $f if $f;
201 0 0         $rec->{seq} = $s if $s;
202              
203 0           my $rc = $self->putrec( 'nv', undef, $rec );
204 0 0         if ( $rc->{nv_id} ) {
205 0           return $rc->{nv_id};
206             } else {
207 0           return $self->{FALSE};
208             }
209             }
210              
211             # getnv ( t, id, n )
212             # returns hash of first rec found
213             # if no n, error
214             sub getnv
215             {
216 0 0   0 0   my $self = shift or return undef;
217 0           my $sn = 'getnv';
218 0           my ( $t, $id, $n ) = @_;
219 0 0         return $self->_error("$sn: missing object type") unless $t;
220 0 0         return $self->_error("$sn: missing object id") unless $id;
221 0 0         return $self->_error("$sn: missing object name") unless $n;
222              
223 0           return $self->sql_select(
224             qq{
225             SELECT * FROM nv WHERE object_type = ? AND object_id = ? AND name = ?
226             }, $t, $id, $n
227             );
228             }
229              
230             # searchnv ( t, id, name, value, attribute )
231             # returns arrayref of hashes { keys: nv_id, object_type, object_id, name, value }
232             # if no n, returns all nvs for t, id
233             sub searchnv
234             {
235 0 0   0 0   my $self = shift or return undef;
236 0           my $sn = 'searchnv';
237 0           my ( $t, $id, $n, $v, $a ) = @_;
238 0 0         return $self->_error("$sn: missing object type") unless $t;
239 0 0         return $self->_error("$sn: missing object id") unless $id;
240 0 0         return $self->_error("$sn: missing nv name") unless $n;
241              
242 0           my $sql = qq{ SELECT * FROM nv WHERE object_type = ? AND object_id = ? AND name = ? };
243              
244 0           my @nvp = ( $t, $id, $n );
245              
246 0 0         if ($v) {
247 0           $sql .= qq{ AND value = ? };
248 0           push( @nvp, $v );
249             }
250              
251 0 0         if ($a) {
252 0           $sql .= qq{ AND attribute = ? };
253 0           push( @nvp, $a );
254             }
255              
256 0           $sql .= qq{ ORDER BY seq };
257              
258 0           return $self->sql_select( $sql, @nvp );
259             }
260              
261             # updnv ( nv_id, value, attribute )
262             # updates rec with new v
263             # returns TRUE (success) or FALSE (failure)
264             sub updnv
265             {
266 0 0   0 0   my $self = shift or return undef;
267 0           my $sn = 'updnv';
268 0           my ( $nv_id, $v, $a ) = @_;
269 0           my $rec;
270 0 0         return $self->_error("$sn: missing nv_id") unless $nv_id;
271 0 0         return $self->_error("$sn: missing value") unless $v;
272              
273 0 0         return $self->_error("$sn: nv $nv_id not found") unless $rec = $self->getrec( 'nv', $nv_id );
274              
275 0 0         $rec->{value} = $v if $v;
276 0 0         $rec->{attribute} = $a if $a;
277              
278 0           return $self->putrec( 'nv', $nv_id, $rec );
279             }
280              
281             # delnv ( nv_id )
282             # deletes nv rec
283             sub delnv
284             {
285 0 0   0 0   my $self = shift or return undef;
286 0           my $sn = 'delnv';
287 0 0         my $nv_id = shift or return $self->_error("$sn: no nv_id");
288              
289 0           return $self->delrec( 'nv', $nv_id );
290             }
291              
292             ### utilty routines
293              
294             # return a list of fields for a given table
295             sub fields_list
296             {
297 0 0   0 0   my $self = shift or return undef;
298 0 0         my $table_name = shift or return undef;
299 0           my $fields_list;
300              
301 0 0         return $fields_list if ( $fields_list = $self->{fields_cache}{$table_name} );
302              
303 0           my $query = " SHOW COLUMNS from $table_name "; # this is likely mysql-specific
304 0           $fields_list = $self->{dbh}->selectcol_arrayref( $query, { Columns => [1] } );
305 0           $self->{fields_cache}{$table_name} = $fields_list;
306 0   0       return $fields_list || undef;
307             }
308              
309             sub gen_id
310             {
311 0 0   0 0   my $self = shift or return undef;
312 0   0       my $id_type = $self->id_type || '';
313 0 0         if ( $id_type eq 'base64' ) {
314 0           return $self->gen_id_base64;
315             } else {
316 0           return $self->gen_id_md5hash;
317             }
318             }
319              
320             sub gen_id_md5hash
321             {
322 0 0   0 0   my $self = shift or return undef;
323 0           return $self->md5hash();
324             }
325              
326             sub gen_id_base64
327             {
328 0 0   0 0   my $self = shift or return undef;
329 0           return $self->md5base64();
330             }
331              
332             sub md5base64 # returns a url-safe base-64 MD5 hash for use as an ID field
333             {
334 0     0 0   my ( $self, $parm ) = @_;
335 0 0         $parm = 'unk' unless $parm;
336 0           my $r = Digest::MD5::md5_base64( $$, time, rand, $parm );
337 0           $r =~ tr|+/|!-|; # '+' and '/' chars are nasty for URLs
338 0           return $r;
339             }
340              
341             sub md5hash
342             {
343 0     0 0   return Digest::MD5::md5_hex( $$, time, rand );
344             }
345              
346             1;
347              
348             =head1 NAME
349              
350             BW::DB::CRUD - Database CRUD
351              
352             =head1 SYNOPSIS
353              
354             Database CRUD: (C)reate, (R)etrieve, (U)pdate, (D)elete
355              
356             use BW::DB::CRUD;
357             my $o = BW::DB::CRUD->new;
358              
359             =head1 METHODS
360              
361             =over 4
362              
363             =item B( )
364              
365             Constructs a new BW::DB::CRUD object.
366              
367             Returns a blessed BW::DB::CRUD object reference.
368             Returns undef (VOID) if the object cannot be created.
369              
370             =item B
371              
372             Returns and clears the object error message.
373              
374             =back
375              
376             =head1 WARNING
377              
378             This module is currently in an unfinished state. Do not use this
379             for new projects. The interface WILL change.
380              
381             =head1 AUTHOR
382              
383             Written by Bill Weinman Ehttp://bw.org/E.
384              
385             =head1 COPYRIGHT
386              
387             Copyright (c) 1995-2010 The BearHeart Group, LLC
388              
389             =head1 HISTORY
390              
391             2010-02-17 bw -- first CPAN version - not ready for prime time!
392             2008-03-27 bw -- cleanup for publishing
393             2007-10-21 bw -- fixed a caching bug in getrec
394             2007-10-19 bw -- initial version.
395              
396             =cut
397