File Coverage

blib/lib/Data/RecordStore/Converter.pm
Criterion Covered Total %
statement 231 232 99.5
branch 36 36 100.0
condition 3 3 100.0
subroutine 46 46 100.0
pod 1 1 100.0
total 317 318 99.6


line stmt bran cond sub pod time code
1             package Data::RecordStore::Converter;
2              
3 1     1   69638 use strict;
  1         3  
  1         25  
4 1     1   5 use warnings;
  1         2  
  1         24  
5              
6 1     1   11 use Data::RecordStore;
  1         3  
  1         22  
7              
8 1     1   5 use File::Copy::Recursive qw( dircopy dirmove );
  1         1  
  1         54  
9 1     1   7 use File::Path qw(remove_tree);
  1         2  
  1         1633  
10              
11              
12             =head2 Data::RecordStore::Converter->convert( $source_dir, $dest_dir )
13              
14             Analyzes the source directory to find version
15             then creates a new version of the store in the
16             destination directory.
17              
18             =cut
19             sub convert {
20 12     12 1 35937 my( $cls, $source_dir, $dest_dir, %args ) = @_;
21              
22 12 100       52 die "Data::RecordStore::Converter->convert must be given a destination directory" unless $dest_dir;
23              
24 11 100       39 die "Data::RecordStore::Converter->convert must be given a source directory" unless $source_dir;
25              
26 10 100       149 die "Data::RecordStore::Converter->convert : cannot find source directory '$source_dir'" unless -d $source_dir;
27            
28 9 100 100     163 if( -d $dest_dir && -e "$dest_dir/VERSION" ) {
29 1         13 die "Data::RecordStore::Converter->convert : Destination directory '$dest_dir' already exists";
30             }
31              
32 8         57 my $source_version = Data::RecordStore->detect_version( $source_dir );
33              
34 8         15 my $rs_pkg;
35 8 100       35 if( ! defined $source_version ) {
36 1         11 die "No store found in $source_dir";
37             }
38 7 100       60 if ( $source_version < 2 ) {
    100          
    100          
    100          
    100          
39 1         2 $rs_pkg = 'RS_1';
40             }
41             elsif ( $source_version < 3 ) {
42 1         4 $rs_pkg = 'RS_2';
43             }
44             elsif ( $source_version < 3.1 ) {
45 1         3 $rs_pkg = 'RS_3';
46             }
47             elsif( $source_version < 4 ) {
48 1         2 $rs_pkg = 'RS_3_1';
49             }
50             elsif( $source_version < 5 ) {
51 1         3 $rs_pkg = 'RS_4';
52             }
53             else { #elsif( $source_version < 6 ) {
54 2         7 $rs_pkg = 'RS_5';
55             }
56              
57 7         44 my $old_rs = $rs_pkg->open( $source_dir );
58              
59 7         27 $args{BASE_PATH} = $dest_dir;
60              
61 7         55 my $new_rs = Data::RecordStore->open_store( %args );
62              
63 7         30 my $entries = $old_rs->entry_count;
64 7         24 for my $id (1..$entries) {
65 314         746 my $val = $old_rs->fetch( $id );
66 314         1030 $new_rs->stow( $val );
67             }
68            
69             } #convert
70              
71             package RS_1;
72              
73             sub open {
74 1     1   3 my( $cls, $dir ) = @_;
75 1         19 my $store_idx = SiloPre3->open( "$dir/STORE_INDEX", "I" );
76 1         4 my $count = $store_idx->entry_count;
77             return bless {
78             count => $count,
79             index_silo => SiloPre3->open( "$dir/OBJ_INDEX", "IL" ),
80 1         6 entry_silos => [ map { SiloPre3->open( "$dir/${_}_OBJSTORE", "A*", $store_idx->get_record($_)->[0] ) } (1..$count) ]
  3         11  
81             }, $cls;
82             }
83             sub entry_count {
84 1     1   13 return shift->{index_silo}->entry_count;
85             }
86             sub fetch {
87 24     24   35 my( $self, $idx ) = @_;
88 24         53 my $lookup = $self->{index_silo}->get_record( $idx );
89 24         59 my( $silo_id, $idx_in_silo ) = @$lookup;
90 24         43 my $silo = $self->{entry_silos}[$silo_id - 1];
91 24         48 my $result = $silo->get_record( $idx_in_silo );
92 24         110 return $result->[0];
93             }
94              
95             package RS_2;
96              
97             sub open {
98 1     1   4 my( $cls, $dir ) = @_;
99              
100 1         24 opendir( my $dh, "$dir/stores/" );
101 1         25 my( @silo_ids ) = map { s/_OBJSTORE//; $_ } grep { /\d+_OBJSTORE/ } readdir( $dh );
  12         20  
  12         20  
  14         35  
102 1         11 closedir $dh;
103            
104             return bless {
105             index_silo => SiloPre3->open( "$dir/OBJ_INDEX", "IL" ),
106 1         11 entry_silos => { map { $_ => SiloPre3->open( "$dir/stores/${_}_OBJSTORE", "LA*", int( exp( $_) ) ) } (@silo_ids)}
  12         42  
107             }, $cls;
108             }
109             sub entry_count {
110 1     1   14 return shift->{index_silo}->entry_count;
111             }
112             sub fetch {
113 49     49   86 my( $self, $idx ) = @_;
114 49         107 my $lookup = $self->{index_silo}->get_record( $idx );
115 49         97 my( $silo_id, $id_in_silo ) = @$lookup;
116 49 100       100 return undef unless $silo_id;
117 37         62 my $silo = $self->{entry_silos}{$silo_id};
118 37         72 my $result = $silo->get_record( $id_in_silo );
119 37         148 return $result->[1];
120             }
121              
122             package RS_3;
123              
124             sub open {
125 1     1   3 my( $cls, $dir ) = @_;
126              
127 1         21 opendir( my $dh, "$dir/silos/" );
128 1         21 my( @silo_ids ) = map { s/_OBJSTORE//; $_ } grep { /\d+_OBJSTORE/ } readdir( $dh );
  12         21  
  12         18  
  14         35  
129 1         11 closedir $dh;
130            
131             return bless {
132             index_silo => Silo3_and_later->open_silo( "IL", "$dir/OBJ_INDEX" ),
133 1         10 entry_silos => { map { $_ => Silo3_and_later->open_silo( "LZ*", "$dir/silos/${_}_OBJSTORE", int( exp( $_) ) ) } (@silo_ids) }
  12         44  
134             }, $cls;
135             }
136             sub entry_count {
137 1     1   8 return shift->{index_silo}->entry_count;
138             }
139             sub fetch {
140 49     49   76 my( $self, $idx ) = @_;
141 49         106 my $lookup = $self->{index_silo}->get_record( $idx );
142 49         93 my( $silo_id, $id_in_silo ) = @$lookup;
143 49 100       93 return undef unless $silo_id;
144 37         68 my $silo = $self->{entry_silos}{$silo_id};
145 37         67 my $result = $silo->get_record( $id_in_silo );
146 37         127 return $result->[1];
147             }
148              
149             package RS_3_1;
150              
151             sub open {
152 1     1   5 my( $cls, $dir ) = @_;
153              
154 1         21 opendir( my $dh, "$dir/silos/" );
155 1         22 my( @silo_ids ) = map { s/_RECSTORE//; $_ } grep { /\d+_RECSTORE/ } readdir( $dh );
  11         17  
  11         20  
  13         33  
156 1         10 closedir $dh;
157            
158             return bless {
159             index_silo => Silo3_and_later->open_silo( "IL", "$dir/RECORD_INDEX_SILO" ),
160 1         12 entry_silos => { map { $_ => Silo3_and_later->open_silo( "LIA*", "$dir/silos/${_}_RECSTORE", int( exp( $_) ) ) } (@silo_ids) }
  11         39  
161             }, $cls;
162             }
163             sub entry_count {
164 1     1   11 return shift->{index_silo}->entry_count;
165             }
166             sub fetch {
167 45     45   83 my( $self, $idx ) = @_;
168 45         100 my $lookup = $self->{index_silo}->get_record( $idx );
169 45         91 my( $silo_id, $id_in_silo ) = @$lookup;
170 45 100       92 return undef unless $silo_id;
171 34         70 my $silo = $self->{entry_silos}{$silo_id};
172 34         65 my $result = $silo->get_record( $id_in_silo );
173 34 100       104 if( $result->[1] ) {
174 9         2028 my $ret = unpack 'u', $result->[2];
175 9         27 chop $ret; #seems that included an extra byte :(
176 9         149 return $ret;
177             }
178 25         64 return $result->[2];
179             }
180              
181             package RS_4;
182              
183             sub open {
184 1     1   5 my( $cls, $dir ) = @_;
185              
186 1         20 opendir( my $dh, "$dir/silos/" );
187 1         18 my( @silo_ids ) = map { s/_RECSTORE//; $_ } grep { /\d+_RECSTORE/ } readdir( $dh );
  5         8  
  5         13  
  7         22  
188 1         10 closedir $dh;
189              
190             return bless {
191             index_silo => Silo3_and_later->open_silo( "IL", "$dir/RECORD_INDEX_SILO" ),
192 1         11 entry_silos => { map { $_ => Silo3_and_later->open_silo( "LA*", "$dir/silos/${_}_RECSTORE", 2 ** $_ ) } (@silo_ids) }
  5         16  
193             }, $cls;
194             }
195             sub entry_count {
196 1     1   13 return shift->{index_silo}->entry_count;
197             }
198             sub fetch {
199 49     49   89 my( $self, $idx ) = @_;
200 49         107 my $lookup = $self->{index_silo}->get_record( $idx );
201 49         102 my( $silo_id, $id_in_silo ) = @$lookup;
202 49 100       97 return undef unless $silo_id;
203 37         76 my $silo = $self->{entry_silos}{$silo_id};
204 37         75 my $result = $silo->get_record( $id_in_silo );
205 37         153 return $result->[1];
206             }
207              
208              
209             package RS_5;
210              
211             sub open {
212 2     2   6 my( $cls, $dir ) = @_;
213            
214 2         40 opendir( my $dh, "$dir/silos/" );
215 2         36 my( @silo_ids ) = map { s/_RECSTORE//; $_ } grep { /\d+_RECSTORE/ } readdir( $dh );
  10         18  
  10         22  
  14         45  
216 2         18 closedir $dh;
217            
218             return bless {
219             index_silo => Silo3_and_later->open_silo( "ILL", "$dir/RECORD_INDEX_SILO" ),
220 2         21 entry_silos => { map { $_ => Silo3_and_later->open_silo( "LA*", "$dir/silos/${_}_RECSTORE", 2 ** $_ ) } (@silo_ids) },
  10         34  
221             }, $cls;
222             }
223             sub entry_count {
224 2     2   18 return shift->{index_silo}->entry_count;
225             }
226             sub fetch {
227 98     98   171 my( $self, $idx ) = @_;
228 98         281 my $lookup = $self->{index_silo}->get_record( $idx );
229 98         202 my( $silo_id, $id_in_silo ) = @$lookup;
230 98 100       206 return undef unless $silo_id;
231 74         139 my $silo = $self->{entry_silos}{$silo_id};
232 74         170 my $result = $silo->get_record( $id_in_silo );
233 74         248 return $result->[1];
234             }
235              
236             package RS_6;
237              
238              
239              
240             package Silo3_and_later;
241              
242 1     1   8 use strict;
  1         2  
  1         24  
243 1     1   5 use warnings;
  1         1  
  1         35  
244 1     1   6 no warnings 'uninitialized';
  1         1  
  1         37  
245 1     1   5 no warnings 'numeric';
  1         1  
  1         39  
246              
247 1     1   5 use Fcntl qw( SEEK_SET );
  1         3  
  1         54  
248 1     1   6 use File::Path qw(make_path remove_tree);
  1         2  
  1         51  
249              
250             use constant {
251 1         134 DIRECTORY => 0,
252             RECORD_SIZE => 1,
253             FILE_SIZE => 2,
254             FILE_MAX_RECORDS => 3,
255             TMPL => 4,
256 1     1   6 };
  1         2  
257              
258             # this really isn't much of a limit anymore, but...
259             # keeping it for now
260             $Silo3_and_later::MAX_SIZE = 2_000_000_000;
261              
262              
263             sub open_silo {
264 43     43   80 my( $class, $template, $directory, $size ) = @_;
265              
266 43         64 my $record_size = $size;
267 43 100       76 if( $record_size == 0 ) {
268 1     1   6 $record_size = do { use bytes; length( pack( $template ) ) };
  1         1  
  1         7  
  5         8  
  5         37  
269             }
270 43         68 my $file_max_records = int( $Silo3_and_later::MAX_SIZE / $record_size );
271 43         59 my $file_max_size = $file_max_records * $record_size;
272              
273 43         92 my $silo = bless [
274             $directory,
275             $record_size,
276             $file_max_size,
277             $file_max_records,
278             $template,
279             ], $class;
280              
281 43         154 return $silo;
282             } #open_silo
283              
284             sub entry_count {
285             # return how many entries this silo has
286 5     5   12 my $self = shift;
287 5         19 my @files = $self->_files;
288 5         8 my $filesize;
289 5         14 for my $file (@files) {
290 5         59 $filesize += -s "$self->[DIRECTORY]/$file";
291             }
292 5         24 return int( $filesize / $self->[RECORD_SIZE] );
293             } #entry_count
294              
295             sub get_record {
296 423     423   634 my( $self, $id ) = @_;
297              
298 423         793 my( $f_idx, $fh, $file, $file_id ) = $self->_fh( $id, 'readonly' );
299              
300 423         1906 sysseek( $fh, $self->[RECORD_SIZE] * $f_idx, SEEK_SET );
301 423         5933 my $srv = sysread $fh, my $data, $self->[RECORD_SIZE];
302 423         2863 close $fh;
303              
304 423         7442 return [unpack( $self->[TMPL], $data )];
305             } #get_record
306              
307             #
308             # Takes an insertion id and returns
309             # an insertion index for in the file
310             # filehandle.
311             # filepath/filename
312             # which number file this is (0 is the first)
313             #
314             sub _fh {
315 423     423   608 my( $self, $id ) = @_;
316 423         673 my @files = $self->_files;
317              
318 423         1220 my $f_idx = int( ($id-1) / $self->[FILE_MAX_RECORDS] );
319              
320 423         561 my $file = $files[$f_idx];
321 423         486 my $fh;
322 423         9515 open( $fh, "<", "$self->[DIRECTORY]/$file" );
323 423         2249 return (($id - ($f_idx*$self->[FILE_MAX_RECORDS])) - 1,$fh,"$self->[DIRECTORY]/$file",$f_idx);
324             } #_fh
325              
326             #
327             # Returns the list of filenames of the 'silos' of this store. They are numbers starting with 0
328             #
329             sub _files {
330 428     428   554 my $self = shift;
331 428         7770 opendir( my $dh, $self->[DIRECTORY] );
332             my( @files ) = (
333 0         0 sort { $a <=> $b }
334 428         5269 grep { /\d+/ }
  1371         4377  
335             readdir( $dh ) );
336 428         3142 closedir $dh;
337 428         1781 return @files;
338             } #_files
339              
340             # ----------- end Silo3_and_later
341              
342             package SiloPre3;
343              
344             #
345             # This package is a helper one that simulates
346             # older silo formats before version 3.1
347             #
348              
349 1     1   430 use strict;
  1         2  
  1         23  
350 1     1   5 use warnings;
  1         2  
  1         20  
351 1     1   5 no warnings 'uninitialized';
  1         18  
  1         37  
352              
353 1     1   5 use Fcntl qw( SEEK_SET );
  1         2  
  1         58  
354              
355             sub open {
356 18     18   34 my( $class, $filename, $template, $size ) = @_;
357 18         25 my $useSize = $size;
358 18 100       29 if( ! $useSize ) {
359 1     1   5 $useSize = do { use bytes; length( pack( $template ) ) };
  1         1  
  1         4  
  3         5  
  3         12  
360             }
361 18         86 bless { TMPL => $template,
362             RECORD_SIZE => $useSize,
363             FILENAME => $filename,
364             }, $class;
365             } #open
366              
367             sub entry_count {
368 3     3   7 my $self = shift;
369 3         34 my $filesize = -s $self->{FILENAME};
370 3         15 return int( $filesize / $self->{RECORD_SIZE} );
371             }
372              
373             sub get_record {
374 137     137   193 my( $self, $id ) = @_;
375              
376 137         195 my $fh = $self->_filehandle;
377 137         601 sysseek $fh, $self->{RECORD_SIZE} * ($id-1), SEEK_SET;
378 137         2059 sysread $fh, my $data, $self->{RECORD_SIZE};
379 137         845 close $fh;
380 137         2212 return [unpack( $self->{TMPL}, $data )];
381             } #get_record
382              
383             sub _filehandle {
384 137     137   168 my $self = shift;
385 137         2853 CORE::open( my $fh, "<", $self->{FILENAME} );
386 137         423 return $fh;
387             }
388              
389              
390              
391              
392             "You can't work in a steel mill and think small. Giant converters hundreds of feet high. Every night, the sky looked enormous. It was a torrent of flames - of fire. The place that Pittsburgh used to be had such scale - Jack Gilbert";
393              
394             __END__