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__ |