File Coverage

/.cpan/build/DBIx-DataLookup-0.03-G_FXeN/blib/lib/DataLookup.pm
Criterion Covered Total %
statement 12 83 14.4
branch 0 22 0.0
condition 0 5 0.0
subroutine 4 10 40.0
pod n/a
total 16 120 13.3


line stmt bran cond sub pod time code
1             package DBIx::DataLookup;
2              
3 1     1   15 use 5.006;
  1         3  
  1         28  
4 1     1   4 use strict;
  1         1  
  1         23  
5 1     1   4 use warnings;
  1         1  
  1         35  
6              
7             require Exporter;
8 1     1   791 use AutoLoader qw(AUTOLOAD);
  1         1449  
  1         6  
9              
10             our @ISA = qw(Exporter);
11              
12             # Items to export into callers namespace by default. Note: do not export
13             # names by default without a very good reason. Use EXPORT_OK instead.
14             # Do not simply export all your public functions/methods/constants.
15              
16             # This allows declaration use DataLookup ':all';
17             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
18             # will save memory.
19             our %EXPORT_TAGS = ( ':all' => [ qw() ],
20             ':default' => [ qw(add_key_mapping get get_hashref) ]);
21              
22             our @EXPORT_OK = ( @{ $EXPORT_TAGS{':default'} } );
23              
24             our @EXPORT = @{ $EXPORT_TAGS{':default'} };
25              
26             our $VERSION = '0.02';
27              
28             # PLAN:
29             # Instantiates an object that will store database data
30             # from a single table (or multiple for that matter, depending
31             # on the kind of SQL statement used to grab that data)
32             # in an internal specially arranged structure to facilitate
33             # quick key value lookup machanism.
34             #
35             # example:
36             # statement = "select col1, col2, col3 from table foobar"
37             # fields = qw(col1 col2 col3)
38             # keys = qw(col2)
39             #
40             # if return data is:
41             #
42             # col1_val1, col2_val1, col3_val1 # record 1
43             # col1_val2, col2_val2, col3_val2 # record 2
44             # col1_val3, col2_val3, col3_val3 # record 3
45             #
46             # Data will be structured as follows:
47             # %table =
48             # (
49             # fields => {
50             # 'col1' => 0,
51             # 'col2' => 1,
52             # 'col3' => 2,
53             # }
54             # records => [
55             # ['col1_val1','col2_val1','col3_val1'], # record 1
56             # ['col1_val2','col2_val2','col3_val2'], # record 2
57             # ['col1_val3','col2_val3','col3_val3'], # record 3
58             # ]
59             # record_keys => {
60             # # col1 serves as key
61             # col1 => {
62             # # key field value => list of matching records
63             # col2_val1 => [0],
64             # col2_val2 => [1],
65             # col2_val3 => [2],
66             # },
67             # }
68             # )
69             #
70             # So, to find a record by a value of col1, you'd have to do this:
71             # name of a key field --\
72             # $table{records}[$table_data{record_keys}{col1}{'col2_val2'}][$table_data{fields}{col3}]
73             #
74             # Which is equivalent to this SQL:
75             #
76             # select col3 from table foobar where col1 = 'col2_val2';
77             #
78             sub new {
79 0     0     my $pkg = shift;
80 0           my $self; { my %hash; $self = bless(\%hash, $pkg); }
  0            
  0            
  0            
81            
82 0           my (%vars) = @_;
83            
84 0           my $ar_keys;
85 0 0         if (exists $vars{keys}) {
86 0 0         if (ref $vars{keys} eq "ARRAY") {
    0          
87 0           @{$ar_keys} = map {uc($_)} @{$vars{keys}};
  0            
  0            
  0            
88             }
89             elsif (ref $vars{keys} eq "SCALAR") {
90 0           $ar_keys = $vars{keys};
91             }
92             }
93              
94 0           my $statement= $vars{statement};
95 0           my $params_aref = $vars{params};
96              
97 0           my $dbh = $vars{dbh};
98 0           my $sth = $dbh->prepare($statement);
99 0 0         $sth->execute(@$params_aref) or die $sth->errstr;
100              
101             # key field(s) (will allow easy hash key lookup).
102             # use the first field by default.
103 0           my $ar_fields = $sth->{NAME};
104              
105             # first field as key by default.
106 0   0       $ar_keys ||= [$ar_fields->[0]];
107              
108 0           my $i = 0;
109 0           %{$self->{table}{fields}} = map {$_ => $i++} @$ar_fields;
  0            
  0            
110              
111 0           while (my @row = $sth->fetchrow_array()) {
112 0           $self->add_record($ar_keys, [@row]);
113             }
114            
115 0           return $self;
116             }
117              
118             #
119             # add a record
120             #
121             # $rec : reference to record array (all field values)
122             # Care should be taken to make sure that
123             # field values are arranged in proper order
124             # here to match the original order in SQL
125             # that was used to build data view (table data).
126             #
127             sub add_record {
128 0     0     my ($self, $ar_keys, $ar_rec) = @_;
129            
130             # store in records hash
131 0           push @{$self->{table}{records}}, $ar_rec;
  0            
132 0           my $record_indx = scalar(@{$self->{table}{records}}) - 1;
  0            
133              
134 0           foreach my $key_field (@$ar_keys) {
135 0           my $key_val = $ar_rec->[$self->{table}{fields}{$key_field}];
136             # create key field mapping
137 0           push @{$self->{table}{record_keys}{$key_field}{$key_val}}, $record_indx;
  0            
138             }
139             }
140              
141             #
142             # maps given key to a record.
143             # Usually done to add new keys that would
144             # link to existing records.
145             #
146             # $key_field : key field name to map
147             # $key_value : new key value
148             # $map_to_value : existing key value
149             #
150             # returns: undef if mapping failed (e.g. no record to map to).
151             sub add_key_mapping {
152 0     0     my ($self, $key_field, $key_value, $map_to_value) = @_;
153              
154 0           $key_field = uc($key_field);
155              
156             # $DB::single = 1;
157             # retrieve matched record indexes
158 0           my $rec_indxs = $self->_find_record_indxs($key_field, $key_value);
159              
160 0 0         return unless ($rec_indxs);
161              
162 0           for my $rec_indx (@$rec_indxs) {
163             # add mapping..
164 0           push @{$self->{table}{record_keys}{$key_field}{$map_to_value}}, $rec_indx;
  0            
165             }
166             }
167              
168             #
169             # returns list of records in the table that
170             # matched key value. Each record is represented
171             # by an array of values.
172             #
173             # Note: actual records are not being copied here.
174             # Therefore, if user chooses to update
175             # a record field value, he/she will be
176             # modifying a record field value stored
177             # in this object's table.
178             #
179             sub get {
180 0     0     my ($self, $key_field, $key_value) = @_;
181 0           my $rec_num = $self->_find_record_indxs($key_field, $key_value);
182 0 0         return ($rec_num) ? @{$self->{table}{records}}[@$rec_num] : ();
  0            
183             }
184              
185             #
186             # get list of references to matched records represented
187             # as hashes.
188             #
189             sub get_hashref {
190 0     0     my ($self, $key_field, $key_value) = @_;
191 0           my $rec_num = $self->_find_record_indxs($key_field, $key_value);
192              
193 0 0         return unless ($rec_num);
194              
195             # note: $self->{table}{records} is array ref therefore,
196             # @{$self->{table}{records}}[@$rec_num] returns one or
197             # more of such array refs (say, if 1 key matched a few
198             # records).
199 0           my @records_arrayrefs = @{$self->{table}{records}}[@$rec_num];
  0            
200              
201 0           my @found_records;
202 0           for my $rec (@records_arrayrefs) {
203             # build table record hash (keys -> column names)
204 0           my %rec_hash;
205 0           $rec_hash{$_} = $rec->[$self->{table}{fields}{$_}] for (keys %{$self->{table}{fields}});
  0            
206 0           push(@found_records, \%rec_hash);
207             }
208              
209             # return reference to the new hash struct.
210 0           return \@found_records;
211             }
212              
213             # returns reference to list of
214             # record indexes which contain the key(s)
215             # $key_value may be an array ref containing
216             # possible keys to compare against.
217             sub _find_record_indxs {
218 0     0     my ($self, $key_field, $key_value) = @_;
219              
220             # return if either of the two required parameters are
221             # not specified.
222 0 0 0       return unless (defined $key_field && defined $key_value);
223              
224 0           my $table = $self->{table};
225 0           $key_field = uc($key_field);
226 0 0         return unless exists $table->{record_keys}{$key_field};
227              
228 0 0         my $keys = (ref $key_value eq "ARRAY") ? $key_value : [$key_value];
229              
230 0           my $rec_num;
231 0           foreach (@$keys) {
232 0 0         if (exists $table->{record_keys}{$key_field}{$_}) {
233 0           return $table->{record_keys}{$key_field}{$_}; # ok, found!
234             }
235             }
236            
237 0           return; # return undef (ak'a 'empty array' or undef based on context)
238             }
239              
240             # Autoload methods go after =cut, and are processed by the autosplit program.
241              
242             1;
243             __END__