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   84579 use strict;
  1         2  
  1         34  
4 1     1   6 use warnings;
  1         6  
  1         24  
5              
6 1     1   14 use Data::RecordStore;
  1         4  
  1         31  
7              
8 1     1   53 use File::Copy::Recursive qw( dircopy dirmove );
  1         6  
  1         75  
9 1     1   7 use File::Path qw(remove_tree);
  1         2  
  1         2033  
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 42773 my( $cls, $source_dir, $dest_dir, %args ) = @_;
21              
22 12 100       60 die "Data::RecordStore::Converter->convert must be given a destination directory" unless $dest_dir;
23              
24 11 100       44 die "Data::RecordStore::Converter->convert must be given a source directory" unless $source_dir;
25              
26 10 100       187 die "Data::RecordStore::Converter->convert : cannot find source directory '$source_dir'" unless -d $source_dir;
27            
28 9 100 100     191 if( -d $dest_dir && -e "$dest_dir/VERSION" ) {
29 1         16 die "Data::RecordStore::Converter->convert : Destination directory '$dest_dir' already exists";
30             }
31              
32 8         58 my $source_version = Data::RecordStore->detect_version( $source_dir );
33              
34 8         21 my $rs_pkg;
35 8 100       34 if( ! defined $source_version ) {
36 1         15 die "No store found in $source_dir";
37             }
38 7 100       67 if ( $source_version < 2 ) {
    100          
    100          
    100          
    100          
39 1         4 $rs_pkg = 'RS_1';
40             }
41             elsif ( $source_version < 3 ) {
42 1         3 $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         3 $rs_pkg = 'RS_3_1';
49             }
50             elsif( $source_version < 5 ) {
51 1         4 $rs_pkg = 'RS_4';
52             }
53             else { #elsif( $source_version < 6 ) {
54 2         7 $rs_pkg = 'RS_5';
55             }
56              
57 7         53 my $old_rs = $rs_pkg->open( $source_dir );
58              
59 7         27 $args{BASE_PATH} = $dest_dir;
60              
61 7         58 my $new_rs = Data::RecordStore->open_store( %args );
62              
63 7         31 my $entries = $old_rs->entry_count;
64 7         27 for my $id (1..$entries) {
65 314         921 my $val = $old_rs->fetch( $id );
66 314         1328 $new_rs->stow( $val );
67             }
68            
69             } #convert
70              
71             package RS_1;
72              
73             sub open {
74 1     1   4 my( $cls, $dir ) = @_;
75 1         8 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         8 entry_silos => [ map { SiloPre3->open( "$dir/${_}_OBJSTORE", "A*", $store_idx->get_record($_)->[0] ) } (1..$count) ]
  3         12  
81             }, $cls;
82             }
83             sub entry_count {
84 1     1   12 return shift->{index_silo}->entry_count;
85             }
86             sub fetch {
87 24     24   51 my( $self, $idx ) = @_;
88 24         68 my $lookup = $self->{index_silo}->get_record( $idx );
89 24         61 my( $silo_id, $idx_in_silo ) = @$lookup;
90 24         50 my $silo = $self->{entry_silos}[$silo_id - 1];
91 24         50 my $result = $silo->get_record( $idx_in_silo );
92 24         134 return $result->[0];
93             }
94              
95             package RS_2;
96              
97             sub open {
98 1     1   5 my( $cls, $dir ) = @_;
99              
100 1         27 opendir( my $dh, "$dir/stores/" );
101 1         29 my( @silo_ids ) = map { s/_OBJSTORE//; $_ } grep { /\d+_OBJSTORE/ } readdir( $dh );
  12         24  
  12         25  
  14         42  
102 1         13 closedir $dh;
103            
104             return bless {
105             index_silo => SiloPre3->open( "$dir/OBJ_INDEX", "IL" ),
106 1         13 entry_silos => { map { $_ => SiloPre3->open( "$dir/stores/${_}_OBJSTORE", "LA*", int( exp( $_) ) ) } (@silo_ids)}
  12         51  
107             }, $cls;
108             }
109             sub entry_count {
110 1     1   13 return shift->{index_silo}->entry_count;
111             }
112             sub fetch {
113 49     49   93 my( $self, $idx ) = @_;
114 49         155 my $lookup = $self->{index_silo}->get_record( $idx );
115 49         122 my( $silo_id, $id_in_silo ) = @$lookup;
116 49 100       135 return undef unless $silo_id;
117 37         72 my $silo = $self->{entry_silos}{$silo_id};
118 37         82 my $result = $silo->get_record( $id_in_silo );
119 37         181 return $result->[1];
120             }
121              
122             package RS_3;
123              
124             sub open {
125 1     1   4 my( $cls, $dir ) = @_;
126              
127 1         31 opendir( my $dh, "$dir/silos/" );
128 1         26 my( @silo_ids ) = map { s/_OBJSTORE//; $_ } grep { /\d+_OBJSTORE/ } readdir( $dh );
  12         26  
  12         25  
  14         44  
129 1         12 closedir $dh;
130            
131             return bless {
132             index_silo => Silo3_and_later->open_silo( "IL", "$dir/OBJ_INDEX" ),
133 1         13 entry_silos => { map { $_ => Silo3_and_later->open_silo( "LZ*", "$dir/silos/${_}_OBJSTORE", int( exp( $_) ) ) } (@silo_ids) }
  12         61  
134             }, $cls;
135             }
136             sub entry_count {
137 1     1   11 return shift->{index_silo}->entry_count;
138             }
139             sub fetch {
140 49     49   89 my( $self, $idx ) = @_;
141 49         150 my $lookup = $self->{index_silo}->get_record( $idx );
142 49         121 my( $silo_id, $id_in_silo ) = @$lookup;
143 49 100       137 return undef unless $silo_id;
144 37         91 my $silo = $self->{entry_silos}{$silo_id};
145 37         81 my $result = $silo->get_record( $id_in_silo );
146 37         154 return $result->[1];
147             }
148              
149             package RS_3_1;
150              
151             sub open {
152 1     1   4 my( $cls, $dir ) = @_;
153              
154 1         26 opendir( my $dh, "$dir/silos/" );
155 1         28 my( @silo_ids ) = map { s/_RECSTORE//; $_ } grep { /\d+_RECSTORE/ } readdir( $dh );
  11         25  
  11         19  
  13         40  
156 1         13 closedir $dh;
157            
158             return bless {
159             index_silo => Silo3_and_later->open_silo( "IL", "$dir/RECORD_INDEX_SILO" ),
160 1         13 entry_silos => { map { $_ => Silo3_and_later->open_silo( "LIA*", "$dir/silos/${_}_RECSTORE", int( exp( $_) ) ) } (@silo_ids) }
  11         42  
161             }, $cls;
162             }
163             sub entry_count {
164 1     1   12 return shift->{index_silo}->entry_count;
165             }
166             sub fetch {
167 45     45   89 my( $self, $idx ) = @_;
168 45         114 my $lookup = $self->{index_silo}->get_record( $idx );
169 45         118 my( $silo_id, $id_in_silo ) = @$lookup;
170 45 100       119 return undef unless $silo_id;
171 34         82 my $silo = $self->{entry_silos}{$silo_id};
172 34         78 my $result = $silo->get_record( $id_in_silo );
173 34 100       109 if( $result->[1] ) {
174 9         2529 my $ret = unpack 'u', $result->[2];
175 9         28 chop $ret; #seems that included an extra byte :(
176 9         192 return $ret;
177             }
178 25         119 return $result->[2];
179             }
180              
181             package RS_4;
182              
183             sub open {
184 1     1   5 my( $cls, $dir ) = @_;
185              
186 1         25 opendir( my $dh, "$dir/silos/" );
187 1         23 my( @silo_ids ) = map { s/_RECSTORE//; $_ } grep { /\d+_RECSTORE/ } readdir( $dh );
  5         12  
  5         12  
  7         26  
188 1         11 closedir $dh;
189              
190             return bless {
191             index_silo => Silo3_and_later->open_silo( "IL", "$dir/RECORD_INDEX_SILO" ),
192 1         13 entry_silos => { map { $_ => Silo3_and_later->open_silo( "LA*", "$dir/silos/${_}_RECSTORE", 2 ** $_ ) } (@silo_ids) }
  5         20  
193             }, $cls;
194             }
195             sub entry_count {
196 1     1   15 return shift->{index_silo}->entry_count;
197             }
198             sub fetch {
199 49     49   101 my( $self, $idx ) = @_;
200 49         122 my $lookup = $self->{index_silo}->get_record( $idx );
201 49         119 my( $silo_id, $id_in_silo ) = @$lookup;
202 49 100       122 return undef unless $silo_id;
203 37         108 my $silo = $self->{entry_silos}{$silo_id};
204 37         87 my $result = $silo->get_record( $id_in_silo );
205 37         194 return $result->[1];
206             }
207              
208              
209             package RS_5;
210              
211             sub open {
212 2     2   8 my( $cls, $dir ) = @_;
213            
214 2         66 opendir( my $dh, "$dir/silos/" );
215 2         42 my( @silo_ids ) = map { s/_RECSTORE//; $_ } grep { /\d+_RECSTORE/ } readdir( $dh );
  10         26  
  10         22  
  14         50  
216 2         23 closedir $dh;
217            
218             return bless {
219             index_silo => Silo3_and_later->open_silo( "ILL", "$dir/RECORD_INDEX_SILO" ),
220 2         25 entry_silos => { map { $_ => Silo3_and_later->open_silo( "LA*", "$dir/silos/${_}_RECSTORE", 2 ** $_ ) } (@silo_ids) },
  10         37  
221             }, $cls;
222             }
223             sub entry_count {
224 2     2   21 return shift->{index_silo}->entry_count;
225             }
226             sub fetch {
227 98     98   204 my( $self, $idx ) = @_;
228 98         248 my $lookup = $self->{index_silo}->get_record( $idx );
229 98         239 my( $silo_id, $id_in_silo ) = @$lookup;
230 98 100       268 return undef unless $silo_id;
231 74         190 my $silo = $self->{entry_silos}{$silo_id};
232 74         175 my $result = $silo->get_record( $id_in_silo );
233 74         312 return $result->[1];
234             }
235              
236             package RS_6;
237              
238              
239              
240             package Silo3_and_later;
241              
242 1     1   9 use strict;
  1         2  
  1         31  
243 1     1   5 use warnings;
  1         2  
  1         43  
244 1     1   9 no warnings 'uninitialized';
  1         2  
  1         38  
245 1     1   7 no warnings 'numeric';
  1         2  
  1         49  
246              
247 1     1   7 use Fcntl qw( SEEK_SET );
  1         2  
  1         67  
248 1     1   6 use File::Path qw(make_path remove_tree);
  1         2  
  1         62  
249              
250             use constant {
251 1         169 DIRECTORY => 0,
252             RECORD_SIZE => 1,
253             FILE_SIZE => 2,
254             FILE_MAX_RECORDS => 3,
255             TMPL => 4,
256 1     1   7 };
  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   101 my( $class, $template, $directory, $size ) = @_;
265              
266 43         67 my $record_size = $size;
267 43 100       91 if( $record_size == 0 ) {
268 1     1   8 $record_size = do { use bytes; length( pack( $template ) ) };
  1         2  
  1         9  
  5         9  
  5         30  
269             }
270 43         79 my $file_max_records = int( $Silo3_and_later::MAX_SIZE / $record_size );
271 43         65 my $file_max_size = $file_max_records * $record_size;
272              
273 43         104 my $silo = bless [
274             $directory,
275             $record_size,
276             $file_max_size,
277             $file_max_records,
278             $template,
279             ], $class;
280              
281 43         214 return $silo;
282             } #open_silo
283              
284             sub entry_count {
285             # return how many entries this silo has
286 5     5   13 my $self = shift;
287 5         24 my @files = $self->_files;
288 5         11 my $filesize;
289 5         16 for my $file (@files) {
290 5         73 $filesize += -s "$self->[DIRECTORY]/$file";
291             }
292 5         29 return int( $filesize / $self->[RECORD_SIZE] );
293             } #entry_count
294              
295             sub get_record {
296 423     423   728 my( $self, $id ) = @_;
297              
298 423         989 my( $f_idx, $fh, $file, $file_id ) = $self->_fh( $id, 'readonly' );
299              
300 423         2246 sysseek( $fh, $self->[RECORD_SIZE] * $f_idx, SEEK_SET );
301 423         7237 my $srv = sysread $fh, my $data, $self->[RECORD_SIZE];
302 423         3310 close $fh;
303              
304 423         9004 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   691 my( $self, $id ) = @_;
316 423         796 my @files = $self->_files;
317              
318 423         1419 my $f_idx = int( ($id-1) / $self->[FILE_MAX_RECORDS] );
319              
320 423         765 my $file = $files[$f_idx];
321 423         548 my $fh;
322 423         11637 open( $fh, "<", "$self->[DIRECTORY]/$file" );
323 423         2797 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   661 my $self = shift;
331 428         9547 opendir( my $dh, $self->[DIRECTORY] );
332             my( @files ) = (
333 0         0 sort { $a <=> $b }
334 428         6485 grep { /\d+/ }
  1371         5375  
335             readdir( $dh ) );
336 428         3838 closedir $dh;
337 428         2206 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   533 use strict;
  1         2  
  1         27  
350 1     1   6 use warnings;
  1         2  
  1         24  
351 1     1   5 no warnings 'uninitialized';
  1         8  
  1         53  
352              
353 1     1   6 use Fcntl qw( SEEK_SET );
  1         3  
  1         70  
354              
355             sub open {
356 18     18   42 my( $class, $filename, $template, $size ) = @_;
357 18         26 my $useSize = $size;
358 18 100       38 if( ! $useSize ) {
359 1     1   7 $useSize = do { use bytes; length( pack( $template ) ) };
  1         2  
  1         4  
  3         9  
  3         12  
360             }
361 18         97 bless { TMPL => $template,
362             RECORD_SIZE => $useSize,
363             FILENAME => $filename,
364             }, $class;
365             } #open
366              
367             sub entry_count {
368 3     3   8 my $self = shift;
369 3         39 my $filesize = -s $self->{FILENAME};
370 3         16 return int( $filesize / $self->{RECORD_SIZE} );
371             }
372              
373             sub get_record {
374 137     137   229 my( $self, $id ) = @_;
375              
376 137         245 my $fh = $self->_filehandle;
377 137         794 sysseek $fh, $self->{RECORD_SIZE} * ($id-1), SEEK_SET;
378 137         2288 sysread $fh, my $data, $self->{RECORD_SIZE};
379 137         1033 close $fh;
380 137         2809 return [unpack( $self->{TMPL}, $data )];
381             } #get_record
382              
383             sub _filehandle {
384 137     137   223 my $self = shift;
385 137         3542 CORE::open( my $fh, "<", $self->{FILENAME} );
386 137         522 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__