File Coverage

lib/Metadata/DB/Base.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Metadata::DB::Base;
2 6     6   37 use strict;
  6         11  
  6         372  
3 6     6   37 use LEOCHARRE::DEBUG;
  6         8  
  6         49  
4 6     6   21332 use LEOCHARRE::DBI;
  0            
  0            
5             use LEOCHARRE::Class2;
6             use warnings;
7             use Carp;
8              
9             __PACKAGE__->make_constructor;
10             __PACKAGE__->make_accessor_setget({
11             table_metadata_name => 'metadata',
12             table_metadata_column_name_id => 'id',
13             table_metadata_column_name_key => 'mkey',
14             table_metadata_column_name_value => 'mval',
15             errstr => undef,
16             });
17              
18              
19             no warnings 'redefine';
20              
21              
22              
23              
24             sub dbh {
25             $_[0]->{DBH} or confess('DBH argument must be provided to constructor.');
26             }
27              
28              
29             # setup
30              
31             sub table_metadata_exists {
32             my $self = shift;
33             return $self->dbh->table_exists($self->table_metadata_name);
34             }
35              
36             sub table_metadata_create {
37             my $self = shift;
38             my $layout = $self->table_metadata_layout;
39             debug("creating table:\n$layout\n");
40             $self->dbh->do($layout)
41             or die( $self->dbh->errstr );
42             return 1;
43             }
44              
45             sub table_metadata_layout {
46             my $self = shift;
47            
48             my $current =
49             sprintf
50             "CREATE TABLE %s (\n"
51             # ." %s varchar(16),\n" # INSTEAD OF CHAR, USE INT, should be quicker
52             ." %s int,"
53             ." %s varchar(32),\n"
54             ." %s varchar(256)\n"
55             .");\n",
56             $self->table_metadata_name,
57             $self->table_metadata_column_name_id,
58             $self->table_metadata_column_name_key,
59             $self->table_metadata_column_name_value
60             ;
61             # this is a strange and interesting layout
62             return $current;
63             }
64              
65             sub table_metadata_drop {
66             my $self = shift;
67             my $table_name = $self->table_metadata_name;
68             $self->dbh->drop_table($table_name)
69             or die($self->dbh->errstr);
70             return 1;
71             }
72              
73             sub table_metadata_reset {
74             my $self = shift;
75             if( $self->table_metadata_exists ){
76             $self->table_metadata_drop;
77             }
78             $self->table_metadata_create;
79             return 1;
80             }
81              
82             # this is mostly debug
83             sub table_metadata_dump { # TODO, i think DBI has this now, alias to that method instead
84             my $self = shift;
85             my $limit = shift; # at most x extries??
86            
87             my $dbh = $self->dbh or die('no dbh() returned');
88             if (defined $limit){
89             $limit = " LIMIT $limit";
90             }
91             $limit||='';
92              
93             my $q = sprintf "SELECT * FROM %s $limit", $self->table_metadata_name;
94             debug("q: $q\n");
95            
96              
97             my $_dump;
98              
99             my $r = $dbh->selectall_arrayref( $q );
100            
101             my $out;
102              
103             my $_id;
104            
105             for(@$r){
106             my ($id,$key,$val) = @$_;
107             if(!$_id or ($id ne $_id)){
108             $out.="\n$id: ";
109             $_id = $id;
110             }
111              
112             $out.=" $key:$val";
113             $_dump->{$id}->{$key} = $val;
114             }
115             $out.="\n\n";
116              
117             return $out;
118             # require Data::Dumper;
119             # my $string = Data::Dumper::Dumper($_dump);
120             # return $string;
121             }
122              
123             sub table_metadata_check { $_[0]->table_metadata_exists or $_[0]->table_metadata_create; 1 }
124              
125              
126              
127              
128             # SINGLE RECORD METHODS, ETC
129              
130              
131             # how many entries does a record hold in the metadata table
132             sub _record_entries_count {
133             my ($self,$id) = @_;
134             defined $id or die('missing id');
135             my $count = $self->dbh->rows_count(
136             $self->table_metadata_name,
137             $self->table_metadata_column_name_id,
138             $id,
139             );
140             return $count;
141             }
142              
143             # delete all entries from db for one record
144             sub _record_entries_delete {
145             my ($self,$id)=@_;
146             defined $id or croak('missing id arg');
147            
148             my $sql = sprintf
149             "DELETE FROM %s WHERE %s=?",
150             $self->table_metadata_name,
151             $self->table_metadata_column_name_id;
152              
153             # what if the table is not there?
154              
155             $self->{_dsth} ||=
156             $self->dbh->prepare($sql)
157             or confess("Could not prepare statement '$sql', ".$self->dbh->errstr);
158            
159             $self->{_dsth}->execute($id);
160             # is do quicker ?
161              
162             # TODO, return count of rows affected??
163             return 1;
164             }
165              
166              
167             *{_record_entries_hashref} = \&_record_entries_hashref_3; # THIS IS THE BEST ONE
168             *{record_entries_hashref} = \&_record_entries_hashref_3; # DONT CHANGE THIS ONE
169              
170             # TODO this needs to be redone to be faster.. somehow
171             sub _record_entries_hashref_1 {
172             my($self,$id)=@_;
173             defined $id or croak('missing id');
174            
175             my $meta={};
176              
177            
178             unless( $self->{_selectall_id} ){
179             my $attribute_return_limit = 100;
180            
181             my $prepped = $self->dbh->prepare(
182             sprintf 'SELECT %s,%s FROM %s WHERE %s = ? LIMIT %s',
183             $self->table_metadata_column_name_key, $self->table_metadata_column_name_value,
184             $self->table_metadata_name, $self->table_metadata_column_name_id, $attribute_return_limit
185             );
186             $self->{_selectall_id} = $prepped;
187             }
188            
189             $self->{_selectall_id}->execute($id);
190              
191            
192             while ( my @row = $self->{_selectall_id}->fetchrow_array ){
193             push @{ $meta->{$row[0]} }, $row[1];
194             }
195             if(DEBUG){
196             my @e = keys %$meta;
197             debug("got elements[@e]\n");
198             }
199             #$self->{_selectall_id}->finish; # maybe this is what's slowing it down
200             # DONT USE finish(), it closes up the statement, means no more will be used of this statement!!!
201              
202             return $meta;
203             }
204              
205             # attempt at making this faster 2 ..
206             sub _record_entries_hashref_2 {
207             my ($self,$id)=@_;
208             defined $id or confess('missing id');
209            
210             my $meta ={};
211            
212              
213             my $sth = $self->dbh->prepare_cached(
214              
215             sprintf 'SELECT %s,%s FROM %s WHERE %s = ?',
216             $self->table_metadata_column_name_key, $self->table_metadata_column_name_value,
217             $self->table_metadata_name, $self->table_metadata_column_name_id
218             );
219            
220             $sth->execute($id);
221              
222            
223             my ($key,$val);
224             # USE BIND COLUMNS, SUPPOSEDLY THE MOST EFFICIENT WAY TO FETCH DATA ACCORDING TO DBI.pm
225             $sth->bind_columns(\$key,\$val);
226              
227             while( $sth->fetch ){
228             push @{$meta->{$key}},$val;
229             }
230             return $meta;
231             }
232              
233              
234             # attempt at making this faster 3 ..
235             sub _record_entries_hashref_3 {
236             my ($self,$id)=@_;
237             defined $id or confess('missing id');
238            
239             my $meta ={};
240            
241             my $_limit = 500; # expect at most how much meta
242             # actually limit is useless unless it is really reached.. :-(
243            
244             $self->{_record_entries_hashref_3} ||=
245             $self->dbh->prepare_cached(
246             sprintf 'SELECT %s,%s FROM %s WHERE %s = ? LIMIT %s',
247             $self->table_metadata_column_name_key, $self->table_metadata_column_name_value,
248             $self->table_metadata_name, $self->table_metadata_column_name_id,
249             $_limit
250             );
251              
252             my $sth = $self->{_record_entries_hashref_3} or die('sth not present');
253            
254             $sth->execute($id);
255            
256             my $_rows = $sth->fetchall_arrayref;
257              
258             for ( @$_rows ){
259             push @{$meta->{$_->[0]}}, $_->[1];
260             }
261            
262             return $meta;
263             }
264              
265              
266              
267              
268             # for lookup
269             # pass it a metadata struct or part of one, and it tries to find ONE
270             # record that matches that one thing
271             sub _find_record_id_via_record_entries_hashref {
272             my ($self, $metaref) = @_;
273             defined $metaref or croak('missing hashref argument');
274              
275             ref $metaref and ref $metaref eq 'HASH' or croak('arg not meta hash ref');
276             # how do we look up if key value is a array ref instead of a single element!!!???
277             # HACK just use first value only for now
278              
279             my ($table,$colk,$colv,$coli) = (
280             $self->table_metadata_name,
281             $self->table_metadata_column_name_key,
282             $self->table_metadata_column_name_value,
283             $self->table_metadata_column_name_id );
284              
285              
286             # subselects
287              
288             # we iterate through the individual att values, the whole array
289             # and build a subquery for each
290             my $sql;
291             my @vals;
292             ATTRIBUTE: for my $att ( keys %$metaref ){
293             ref $metaref->{$att} and ref $metaref->{$att} eq 'ARRAY'
294             or croak("meta ref elements must be array refs, as returned by get_all()");
295              
296              
297              
298             VALUE: for my $val ( @{$metaref->{$att}} ){
299             defined $val or warn("not defined '$att'") and next VALUE;
300            
301            
302             if( !$sql ){ # first entry not yet made, this will be inner select
303             $sql = "SELECT $coli FROM $table WHERE $colk=? AND $colv=?";
304             }
305              
306             else { # deeper level, thus subselect
307             # http://dev.mysql.com/doc/refman/5.0/en/subquery-restrictions.html
308             $sql = "SELECT $coli FROM $table WHERE $colk=? AND $colv=? AND $coli IN ( $sql )";
309              
310             }
311              
312             push @vals, $att, $val;
313             }
314              
315             }
316              
317             # add limit
318             $sql.=" LIMIT 2"; # because if we have more than one we know the search is too lose
319             # also without a limit, will keep searching. we just want to know if there's only one
320             # that matches these params.
321              
322             debug("query: \n $sql\nvals: \n@vals\n");
323             my $sth = $self->dbh->prepare( $sql ) or die();
324             debug("prepared.");
325             $sth->execute(@vals);
326             my @ids;
327             while ( my $row = $sth->fetch ){
328             push @ids, $row->[0];
329             }
330              
331             my $hits = scalar @ids;
332              
333             $hits or $self->errstr('None found') and return;
334             $hits == 1 or $self->errstr("Too many found [$hits], narrow your search.") and return;
335            
336             $ids[0];
337             }
338              
339              
340              
341              
342              
343              
344              
345             sub _table_metadata_insert {
346             my($self,$id,$key,$val)=@_;
347             defined $val or confess('missing value arg');
348              
349             unless ( $self->{_table_metadata_insert} ){
350            
351             my $q = sprintf
352             'INSERT INTO %s (%s,%s,%s) values (?,?,?)',
353             $self->table_metadata_name,
354             $self->table_metadata_column_name_id,
355             $self->table_metadata_column_name_key,
356             $self->table_metadata_column_name_value;
357            
358             $self->{_table_metadata_insert} = $self->dbh->prepare( $q );
359              
360             }
361              
362            
363             $self->{_table_metadata_insert}->execute( $id, $key, $val);
364             return 1;
365             }
366              
367              
368             # inject metadata hashref for an id
369             sub _table_metadata_insert_multiple {
370             my($self,$id,$meta_hashref) = @_;
371             ref $meta_hashref eq 'HASH' or croak('missing meta hash ref arg');
372              
373             my @atts = keys %$meta_hashref
374             or croak("there are no key value pairs in the hashref");
375              
376             ATTRIBUTE : for my $att ( @atts ){
377             my $_val = $meta_hashref->{$att};
378             defined $_val or debug("att $att was not defined\n") and next ATTRIBUTE;
379             if ( ref $_val eq 'ARRAY' ){
380             debug("$att is array ref");
381             for ( @$_val ){
382             $self->_table_metadata_insert( $id, $att, $_ );
383             }
384             next ATTRIBUTE;
385             }
386             elsif( ref $_val ){
387             confess(__PACKAGE__.' _table_metadata_insert_multiple(), the value you want to insert into the metadata table is not an array or scalar');
388             }
389            
390             debug("$att is scalar");
391             $self->_table_metadata_insert( $id, $att, $_val );
392             }
393             return 1;
394             }
395              
396              
397             sub create_index_id {
398             my $self = shift;
399             $self->create_index(
400             $self->table_metadata_name,
401             $self->table_metadata_column_name_id
402             );
403             debug('created index 1');
404             return 1;
405             }
406              
407             sub create_index {
408             my($self, $tablename, $colname) = @_;
409            
410             defined $colname or croak('missing colum name argument');
411             my $cmd = "CREATE INDEX $colname\_index ON $tablename($colname);";
412             debug($cmd);
413             $self->dbh->do($cmd) or die("Could not dbh do '$cmd',".$self->dbh->errstr);
414             return 1;
415             }
416              
417             sub table_metadata_last_record_id {
418             my $self = shift;
419             my ( $col_id, $table_name) = ( $self->table_metadata_column_name_id, $self->table_metadata_name );
420              
421             my $sql = "SELECT $col_id FROM $table_name ORDER BY $col_id DESC LIMIT 1";
422             my $aref = $self->dbh->selectall_arrayref($sql) or croak("Could not '$sql', ".$self->dbh->errstr);
423             $aref and scalar @$aref or $self->errstr("Didn't find anything, no records?") and return;
424             $aref->[0]->[0];
425             }
426              
427              
428             1;
429              
430              
431             __END__